Compare commits

...

No commits in common. "main" and "v0.0.3" have entirely different histories.
main ... v0.0.3

674 changed files with 253206 additions and 60082 deletions

3
.gitignore vendored
View file

@ -6,6 +6,5 @@ __pycache__
.wheels .wheels
.vendor .vendor
.python3-deb .python3-deb
SRV/storage/subnets/* SRV/storage/devices/devices.json
SRV/storage/wifi/wifi.json SRV/storage/wifi/wifi.json
build/

View file

@ -1,137 +0,0 @@
when:
event: manual
steps:
checkout:
image: debian:latest
commands:
# ПОДГОТОВКА ГИТА И КЛОНИРОВАНИЕ GONETS
- apt update
- apt install -y git git-lfs
- git lfs install
# УСТАНОВКА НЕОБХОДИМЫХ ЗАВИСИМОСТЕЙ НА СИСТЕМЕ
- apt update
- apt install -y python3 curl findutils grep sed openssh-server tree libnss3 libnss3-dev libnspr4 libnspr4-dev libasound2 libasound2-dev python3.11-venv python3-pip python3-poetry nodejs npm debhelper devscripts build-essential fakeroot dh-make dpkg-dev cmake libgirepository1.0-dev gobject-introspection libcairo2-dev pkg-config python3-dev libdbus-1-dev
- python3 --version
- node -v
- npm -v
raspberry
# ПОДГОТОВКА СПИСКА PYTHON ЗАВИСИМОСТЕЙ GONETS
- rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED
- rm -rf /root/.local/share/pypoetry
- rm -rf /root/.poetry
- rm -rf /usr/bin/poetry
- pip install pipx
- pipx ensurepath
- pipx install poetry
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc
- . /root/.bashrc
- poetry --version
- cd SRV
- poetry env activate
- poetry add poetry-plugin-export
- poetry install
- poetry self update
- rm -f requirements.txt
- poetry run poetry export -f requirements.txt --output requirements.txt --without-hashes
# ПОДГОТОВКА И СБОРКА FRONTEND ЧАСТИ GONETS
- pwd
- cd ..
- pwd
- cd FRONT/DEV
- pwd
- npm install
- npm run package
- cd dist/gonets-linux-x64/resources/app
- pwd
- npm run build
- sed -i 's#href="/assets/#href="./assets/#g' dist/index.html
- sed -i 's#src="/assets/#src="./assets/#g' dist/index.html
- cd ../../../../..
- pwd
- rm -rf BUILD/*
- cp -r DEV/dist/gonets-linux-x64/* BUILD/
- rm -rf DEV
# ИНИЦИАЛИЗАЦИЯ DEBIAN КОНФИГУРАЦИЙ ДЛЯ СБОРКИ
- cd ..
- pwd
- cd ..
- pwd
- mv gonets gonets-0.1
- cd gonets-0.1
- pwd
- ls
- DEBEMAIL="devteam@gossys.dev" DEBFULLNAME="GOSSYS LLC" dh_make --single -y --createorig
- cd debian/
- rm -f *.ex
- rm -f README.*
# КЛОНИРОВАНИЕ НЕОБХОДИМЫХ ШАБЛОНОВ КОНФИГОВ ДЕБИАНА ДЛЯ СБОРКИ
- mkdir -p /woodpecker/src/dev.gossys.dev/HybrydMain/templates
- git clone https://s.galanskiy:${GONETS}@dev.gossys.dev/s.galanskiy/gonets-debian-template.git /woodpecker/src/dev.gossys.dev/HybrydMain/templates
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/changelog /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/control /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/copyright /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/install /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/postinst /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/postrm /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
- mv /woodpecker/src/dev.gossys.dev/HybrydMain/templates/rules /woodpecker/src/dev.gossys.dev/HybrydMain/gonets-0.1/debian/
# СБОРКА ДЕБИАН ПАКЕТА
- cd ..
- pwd
- dpkg-buildpackage -us -uc -b
# ИЗВЛЕЧЕНИЕ ВЕРСИИ ПАКЕТА ДЛЯ КОММИТА
- VERSION=$(dpkg-parsechangelog -l./debian/changelog --show-field Version)
- echo "VERSION=$VERSION" > version.env
# КОММИТ И ТЕГ НА ВЕТКУ DEVELOP
- . ./version.env
- git config user.name "s.galanskiy"
- git config user.email "s.galanskiy@gossys.dev"
- git fetch --all --prune
- git branch -r
- git checkout -b "release/$VERSION"
- git commit -am "Релиз $VERSION"
- git tag -f "v$VERSION"
- git push "https://s.galanskiy:b4785198d18200dfed812dc4299575f736255bd7@dev.gossys.dev/HybrydMain/gonets.git" "release/$VERSION"
- git push -f https://s.galanskiy:b4785198d18200dfed812dc4299575f736255bd7@dev.gossys.dev/HybrydMain/gonets.git "v$VERSION"
# СОЗДАНИЕ И ЗАГРУЗКА РЕЛИЗА ЧЕРЕЗ API
- |
VERSION=$(dpkg-parsechangelog -l./debian/changelog --show-field Version)
echo "VERSION=$VERSION" > version.env
# Задать переменные
. ./version.env
TAG="v$VERSION"
DEB_PATH=$(find .. -maxdepth 1 -type f -name "gonets_*_amd64.deb" | head -n 1)
if [ ! -f "$DEB_PATH" ]; then
echo ".deb файл не найден по пути: $DEB_PATH"
exit 1
fi
RELEASE_JSON=$(curl -s -X POST "https://dev.gossys.dev/api/v1/repos/HybrydMain/gonets/releases" \
-H "Authorization: token b4785198d18200dfed812dc4299575f736255bd7" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"$TAG\", \"name\": \"Релиз $TAG\", \"body\": \"Сборка .deb пакета для версии $TAG\", \"draft\": false, \"prerelease\": true}")
RELEASE_ID=$(echo "$RELEASE_JSON" | grep '"id":' | head -n1 | sed 's/[^0-9]*\([0-9]*\).*/\1/')
if [ -z "$RELEASE_ID" ]; then
echo "Не удалось получить ID релиза из ответа:"
echo "$RELEASE_JSON"
exit 1
fi
curl -s -X POST \
-H "Authorization: token b4785198d18200dfed812dc4299575f736255bd7" \
-F "attachment=@$DEB_PATH" \
"https://dev.gossys.dev/api/v1/repos/HybrydMain/gonets/releases/$RELEASE_ID/assets" || echo "Ошибка загрузки файла"

View file

@ -1,13 +0,0 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GoNets</title>
<link rel="icon" type="image/x-icon" href="/assets/icon-4fEsvohP.png" />
<script type="module" crossorigin src="/assets/index-QSWEVNb0.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DWFkiel1.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View file

@ -100,7 +100,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
<CardContent isExpanded={isExpanded}> <CardContent isExpanded={isExpanded}>
<InfoRow> <InfoRow>
<Label>Имя:</Label> <Label>Имя:</Label>
<Value>{device.hostname || 'Неизвестно'}</Value> <Value>{device.hostname || device.ip}</Value>
</InfoRow> </InfoRow>
<InfoRow> <InfoRow>

View file

@ -87,7 +87,7 @@ const TableScanner = ({devices}:TableScannerProps) => {
{devices.map((item, i) => ( {devices.map((item, i) => (
<TableRow key={i}> <TableRow key={i}>
<TableCell><StatusBadge status={item.status}></StatusBadge></TableCell> <TableCell><StatusBadge status={item.status}></StatusBadge></TableCell>
<TableCell>{item.hostname || 'Неизвестен'}</TableCell> <TableCell>{item.hostname || item.ip}</TableCell>
<TableCell>{item.vendor}</TableCell> <TableCell>{item.vendor}</TableCell>
<TableCell>{item.ip}</TableCell> <TableCell>{item.ip}</TableCell>
<TableCell>{item.open_ports.join(', ') || '-'}</TableCell> <TableCell>{item.open_ports.join(', ') || '-'}</TableCell>

View file

@ -31,7 +31,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
return ( return (
<> <>
<ScanBtn onClick={handleScan} disabled={loading}> <ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'} {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы подсети'}
</ScanBtn> </ScanBtn>
{devices.length > 0 ? ( {devices.length > 0 ? (

View file

@ -30,7 +30,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
return ( return (
<> <>
<ScanBtn onClick={handleScan} disabled={loading}> <ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'} {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы подсети'}
</ScanBtn> </ScanBtn>
{devices.length > 0 ? ( {devices.length > 0 ? (

View file

@ -163,7 +163,7 @@ export default function UserScanner() {
return ( return (
<Wrapper scanInitiated={scanInitiated} loading={loading}> <Wrapper scanInitiated={scanInitiated} loading={loading}>
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование пользователей в сети</TitleWrapper>)} {(!scanInitiated || loading) && (<TitleWrapper>Сканирование узлов в подсети</TitleWrapper>)}
<UserLocate loading={loading} scanInitiated={scanInitiated}> <UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center"></span> <span className="center"></span>
<span className="ring1"></span> <span className="ring1"></span>
@ -177,7 +177,7 @@ export default function UserScanner() {
{loading ? ( {loading ? (
<LoadingText> <LoadingText>
Сканирование сети<ScanningText /> Сканирование узлов в подсети<ScanningText />
</LoadingText> </LoadingText>
) : selectedView.value == 1 ? ( ) : selectedView.value == 1 ? (
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} /> <TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />

View file

@ -161,7 +161,7 @@ export function WifiScanner() {
return ( return (
<Wrapper scanInitiated={scanInitiated} loading={loading}> <Wrapper scanInitiated={scanInitiated} loading={loading}>
<TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper> <TitleWrapper>Сканирование точек доступа Wi-Fi</TitleWrapper>
<UserLocate loading={loading} scanInitiated={scanInitiated}> <UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center circel"></span> <span className="center circel"></span>
@ -186,11 +186,11 @@ export function WifiScanner() {
{loading ? ( {loading ? (
<LoadingText> <LoadingText>
Сканирование сети <ScanningText/> Сканирование точек доступа <ScanningText/>
</LoadingText> </LoadingText>
) : ( ) : (
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }> <ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'} {wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
</ScanBtn> </ScanBtn>
)} )}
</Wrapper> </Wrapper>

View file

@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
setHasInited(false) setHasInited(false)
initializationAttempt.current = 0; initializationAttempt.current = 0;
const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`);
let connectionTimeout; let connectionTimeout;
setSocket(newSocket); setSocket(newSocket);

View file

@ -115,7 +115,7 @@ export const DevicesBar = () => {
const checkWebSocketAvailability = async (ip: string): Promise<boolean> => { const checkWebSocketAvailability = async (ip: string): Promise<boolean> => {
return new Promise((resolve) => { return new Promise((resolve) => {
const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`);
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
testSocket.close(); testSocket.close();
resolve(false); resolve(false);

View file

@ -1,61 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter } from 'react-router-dom';
import { useIpStore } from './src/store';
import { useEffect } from 'react';
import { useCheckAdmin } from './src/utils/checkAdmin';
import {
ModalProvider,
useModalContext,
ThemeProvider
} from './src/store';
import WindowComponent from './src/components/modalwindows/modalWindow';
import Router from './src/router';
import { I18nProvider } from "./src/utils/i18n/provider";
import { useLanguage } from "./src/hooks/useSettings";
const AppWithProviders = () => {
const currentLanguage = useLanguage();
return (
<I18nProvider initialLanguage={currentLanguage}>
<ThemeProvider>
<HashRouter>
<ModalProvider>
<MainComponent />
</ModalProvider>
</HashRouter>
</ThemeProvider>
</I18nProvider>
);
};
const MainComponent = () => {
const checkAdmin = useCheckAdmin();
const { ipAddress } = useIpStore();
useEffect(() => {
const checkAdminStatus = async () => {
await checkAdmin();
};
checkAdminStatus();
}, [ipAddress]);
const { isOpen, modalType, modalData } = useModalContext();
return (
<>
<WindowComponent isOpen={isOpen} type={modalType} data={modalData} />
<Router />
</>
);
};
const container = document.getElementById('root');
if (!container) {
throw new Error('Root element with id "root" not found');
}
const root = createRoot(container);
root.render(<AppWithProviders />);

File diff suppressed because it is too large Load diff

View file

@ -1,11 +0,0 @@
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => ipcRenderer.send(channel, data),
on: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args)),
quitApp: () => ipcRenderer.send('quit-app'),
showSaveDialog: (options) => ipcRenderer.invoke('show-save-dialog', options),
writeFile: (path, data) => ipcRenderer.invoke('write-file', { path, data }),
onThemeChange: (callback) => ipcRenderer.on('theme-update', (event, theme) => callback(theme))
});

View file

@ -1,186 +0,0 @@
{
"userScanner-title": "Subnet Node Scanning",
"userScanner-visible": "Display Method",
"userScanner-loading": "Scanning nodes",
"userScanner-refresh": "Refresh List",
"userScanner-scan": "Scan Nodes",
"userScanner-notFound": "Nodes not found",
"tableScanner-online": "Online",
"tableScanner-name": "Name",
"tableScanner-vendor": "Vendor",
"tableScanner-ip": "IP Address",
"tableScanner-ports": "Open Ports",
"tableScanner-activity": "Last Activity",
"tableScanner-note": "Note",
"tableScanner-action": "Action",
"tableScanner-unknown": "Unknown",
"tableScanner-noData": "No data to display",
"listUsers-activeNodes": "Active Nodes",
"listUsers-inactiveNodes": "Inactive Nodes",
"ipInput-placeholder": "Enter IP",
"ipInput-error": "Enter a valid IP address",
"userScanner-visible_table": "Table",
"userScanner-visible_list": "List",
"wifiScanner-title": "Wi-Fi Access Point Scanning",
"wifiScanner-scanning": "Scanning access points",
"wifiScanner-scanError": "Network scanning error",
"wifiScanner-refresh": "Refresh List",
"wifiScanner-scan": "Scan Access Points",
"wifiModal-bssid": "BSSID",
"wifiModal-vendor": "Vendor",
"wifiModal-signal": "Signal",
"wifiModal-channel": "Channel",
"wifiModal-encryption": "Encryption",
"wifiModal-frequency": "Frequency",
"wifiModal-hidden": "Hidden Network",
"wifiModal-note": "Note",
"wifiModal-connect": "Connect",
"wifiModal-disconnect": "Disconnect",
"wifiModal-unknown": "Unknown",
"wifiModal-none": "None",
"common-yes": "Yes",
"common-no": "No",
"addNode": "Add Node",
"enterNodeAddress": "Enter node address...",
"connectionLost": "Connection Lost",
"serverDisconnected": "Server connection interrupted",
"checkAddressAndRetry": "Please check the address availability and try again",
"cancel": "Cancel",
"close": "Close",
"save": "Save",
"retry": "Retry",
"connect": "Connect",
"submit": "Submit",
"common-back": "Back",
"error": "Error",
"networkConnection": "Network Connection",
"enterPassword": "Enter password...",
"connectingAttempt": "Attempting to connect to the network",
"connectionError": "Network connection error",
"connectionErrorLog": "Connection error: ",
"customizeTable-title": "Subnet Node Table Customization",
"wifiDisconnect-title": "Wi-Fi Network Disconnection",
"wifiDisconnect-confirm": "Confirm Disconnection",
"wifiDisconnect-message1": "You are about to disconnect from the network - ",
"wifiDisconnect-message2": " All active connections will be interrupted.",
"wifiDisconnect-disconnect": "Disconnect",
"wifiDisconnect-progress": "Disconnecting from the network",
"ipChange-title": "IP Address Change",
"ipChange-checkingIp": "Checking IP address validity",
"ipChange-generalInfo": "General Information",
"ipChange-interface": "Interface",
"ipChange-ipInfo": "IP Information",
"ipChange-currentIp": "Current IP",
"ipChange-currentGateway": "Current Gateway",
"ipChange-currentDns": "Current DNS",
"ipChange-newIp": "New IP",
"ipChange-newGateway": "New Gateway",
"ipChange-newDns": "New DNS",
"ipChange-checkError": "An error occurred while checking validity",
"ipChange-adminRequired": "To change network settings, you must log in as an administrator",
"note-edit": "Edit Note",
"note-add": "Add Note",
"note-enter": "Enter note...",
"auth-successLogin": "Successful Login",
"auth-successLogout": "Successful Logout",
"auth-getAccess": "Obtaining Access Rights",
"auth-enableAdmin": "Enable Administrative Access",
"auth-enterAdminPass": "Enter administrator password...",
"auth-wrongPass": "Incorrect operator password",
"auth-switchToLimited": "Switch to Limited Access",
"auth-limitedModeDesc": "In this mode, administrator privileges are restricted.",
"auth-restrictAccess": "Restrict Access",
"auth-closeHint": "This window can be closed, or it will disappear on its own",
"operatorConfig-title": "Node Operator Configuration",
"operatorConfig-instruction": "Before starting, you need to create an application operator password",
"operatorConfig-passwordPlaceholder": "Enter password...",
"operatorConfig-repeatPasswordPlaceholder": "Repeat password...",
"operatorConfig-passwordMismatch": "Passwords do not match!",
"operatorConfig-submit": "Submit",
"about-logoAlt": "Gossys Logo",
"about-title": "About the Application",
"about-description": "GoNets is a minimally viable cross-platform product (MVP) for demonstrating our approach to managing PC settings in a corporate environment.",
"about-featuresTitle": "Features",
"about-feature1": "Visualization of active network resources",
"about-feature2": "Configuration of local and remote network resource parameters",
"about-feature3": "Management of resource interaction with the GoNets Client",
"about-disclaimer": "The software is for demonstration and home use only.",
"about-goToPro": "Go to GoNets-PRO",
"about-homepage": "Homepage",
"about-documentation": "Documentation",
"about-license": "License",
"about-copyright": "Developed and maintained by Hybrid Office Systems LLC, 2024-2025. All rights reserved.",
"main-logoAlt": "Application Logo",
"main-networkSettings": "Network Settings",
"main-about": "About the Program",
"nodes-availableNodes": "Available Nodes",
"nodes-rootAddress": "Root Address",
"nodes-name": "Name",
"nodes-loading": "Loading",
"nodes-additionalNodes": "Additional Nodes",
"nodes-node": "Node",
"nodes-address": "Address",
"nodes-connectionError": "Failed to connect to the server at the address ",
"settings-nodesSection": "Subnet Nodes",
"settings-intervalScanning": "Interval Scanning",
"settings-scanInterval": "Node Scan Interval (min)",
"settings-displayNames": "Display Names When Missing",
"settings-ipAddress": "IP Address",
"settings-unknown": "Unknown",
"settings-rowsPerPage": "Rows Per Table",
"settings-displaySettings": "Nodes Display",
"settings-configure": "Configure",
"settings-clearSessionData": "Clear Session Data",
"settings-clear": "Clear",
"settings-clearFailed": "Data was not deleted: ",
"settings-exportData": "Export Nodes Data",
"settings-export": "Export",
"settings-saveNodesTitle": "Save Nodes Data",
"settings-exportSuccess": "Data exported successfully!",
"settings-exportError": "Export error: ",
"settings-status": "Online",
"settings-name": "Name",
"settings-vendor": "Manufacturer",
"settings-openPorts": "Open Ports",
"settings-lastActivity": "Last Activity",
"settings-note": "Note",
"settings-action": "Action",
"settings-title": "Settings",
"settings-resetSettings": "Reset Settings",
"settings-resetConfirm": "Are you sure you want to reset all settings to default values?",
"settings-cancel": "Cancel",
"settings-save": "Save",
"settings-fixErrors": "Fix errors before saving",
"settings-fillRequired": "Fill in all required fields",
"settings-dataNotDeleted": "Data was not deleted: ",
"settings-saveNetworksTitle": "Save Networks Data",
"settings-fieldRequired": "Field cannot be empty",
"settings-enterNumber": "Please enter a number",
"settings-valueTooSmall": "Value cannot be less than ",
"settings-valueTooLarge": "Value cannot be greater than ",
"settings-sections-subnetNodes": "Subnet Nodes",
"settings-sections-accessPoints": "Access Points",
"settings-sections-appearance": "Appearance",
"settings-exportNetworkData": "Export Access Points Data",
"settings-colorTheme": "Color Theme",
"settings-light": "Light",
"settings-dark": "Dark",
"settings-system": "As in System",
"settings-interfaceLanguage": "Interface Language",
"settings-russian": "Russian",
"settings-english": "English"
}

View file

@ -1,192 +0,0 @@
{
"userScanner-title": "Сканирование узлов подсети",
"userScanner-visible": "Способ отображения",
"userScanner-loading": "Cканирование узлов",
"userScanner-refresh": "Обновить список",
"userScanner-scan": "Сканировать узлы",
"userScanner-notFound": "Узлы не найдены",
"tableScanner-online": "Онлайн",
"tableScanner-name": "Имя",
"tableScanner-vendor": "Производитель",
"tableScanner-ip": "IP адрес",
"tableScanner-ports": "Открытые порты",
"tableScanner-activity": "Последняя активность",
"tableScanner-note": "Заметка",
"tableScanner-action": "Действие",
"tableScanner-unknown": "Неизвестно",
"tableScanner-noData": "Нет данных для отображения",
"listUsers-activeNodes": "Активные узлы",
"listUsers-inactiveNodes": "Неактивные узлы",
"ipInput-placeholder": "Введите IP",
"ipInput-error": "Введите корректный IP-адрес",
"userScanner-visible_table": "Таблица",
"userScanner-visible_list": "Список",
"wifiScanner-title": "Сканирование точек доступа Wi-Fi",
"wifiScanner-scanning": "Сканирование точек доступа",
"wifiScanner-scanError": "Ошибка сканирования сети",
"wifiScanner-refresh": "Обновить список",
"wifiScanner-scan": "Сканировать точки доступа",
"wifiModal-bssid": "BSSID",
"wifiModal-vendor": "Производитель",
"wifiModal-signal": "Сигнал",
"wifiModal-channel": "Канал",
"wifiModal-encryption": "Шифрование",
"wifiModal-frequency": "Частота",
"wifiModal-hidden": "Скрытая сеть",
"wifiModal-note": "Заметка",
"wifiModal-connect": "Подключиться",
"wifiModal-disconnect": "Отключиться",
"wifiModal-unknown": "Неизвестно",
"wifiModal-none": "Отсутствует",
"common-yes": "Да",
"common-no": "Нет",
"addNode": "Добавление узла",
"enterNodeAddress": "Введите адрес узла...",
"connectionLost": "Соединение прервано",
"serverDisconnected": "Произошёл разрыв с сервером",
"checkAddressAndRetry": "Пожалуйста, проверьте доступ к адресу и попробуйте снова",
"cancel": "Отменить",
"close": "Закрыть",
"save": "Сохранить",
"retry": "Повторить",
"connect": "Подключиться",
"common-back": "Назад",
"submit": "Подтвердить",
"error": "Ошибка",
"networkConnection": "Подключение к сети",
"enterPassword": "Введите пароль...",
"connectingAttempt": "Попытка подключения к сети",
"connectionError": "Ошибка подключения к сети",
"connectionErrorLog": "Ошибка подключения: ",
"customizeTable-title": "Кастомизация таблицы узлов подсети",
"wifiDisconnect-title": "Отключение от Wi-Fi сети",
"wifiDisconnect-confirm": "Подтверждение отключения",
"wifiDisconnect-message1": "Вы собираетесь отключиться от сети - ",
"wifiDisconnect-message2": " Все активные подключения будут прерваны.",
"wifiDisconnect-disconnect": "Отключиться",
"wifiDisconnect-progress": "Отключение от сети",
"ipChange-title": "Изменение IP-Адреса",
"ipChange-checkingIp": "Идет проверка корректности IP адреса",
"ipChange-generalInfo": "Общие сведения",
"ipChange-interface": "Интерфейс",
"ipChange-ipInfo": "Сведения об IP",
"ipChange-currentIp": "Текущий ip",
"ipChange-currentGateway": "Текущий шлюз",
"ipChange-currentDns": "Текущий DNS",
"ipChange-newIp": "Новый ip",
"ipChange-newGateway": "Новый шлюз",
"ipChange-newDns": "Новый DNS",
"ipChange-checkError": "Произошла ошибка проверки корректности",
"ipChange-adminRequired": "Для изменения сетевых настроек необходимо авторизироваться под администратором",
"note-edit": "Редактирование заметки",
"note-add": "Добавление заметки",
"note-enter": "Введите заметку...",
"auth-successLogin": "Успешный вход",
"auth-successLogout": "Успешный выход",
"auth-getAccess": "Получение прав доступа",
"auth-enableAdmin": "Включить административный доступ",
"auth-enterAdminPass": "Введите пароль администратора...",
"auth-wrongPass": "Неверный пароль оператора",
"auth-switchToLimited": "Переключиться на ограниченный доступ",
"auth-limitedModeDesc": "В данном режиме доступа ограничиваются полномочия администратора.",
"auth-restrictAccess": "Ограничить доступ",
"auth-closeHint": "Это окно можно закрыть, либо оно пропадёт само",
"operatorConfig-title": "Настройка оператора узла",
"operatorConfig-instruction": "Перед началом работы необходимо создать пароль оператора приложения",
"operatorConfig-passwordPlaceholder": "Введите пароль...",
"operatorConfig-repeatPasswordPlaceholder": "Повторите пароль...",
"operatorConfig-passwordMismatch": "Пароли не совпадают!",
"operatorConfig-submit": "Отправить",
"about-logoAlt": "Логотип Gossys",
"about-title": "О приложении",
"about-description": "ГоNets - минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.",
"about-featuresTitle": "Возможности",
"about-feature1": "Визуализация активных сетевых ресурсов",
"about-feature2": "Настройка параметров локальных и удалённых сетевых ресурсов",
"about-feature3": "Управление взаимодействием ресурсов с Клиентом ГoNets",
"about-disclaimer": "Программное обеспечение исключительно для ознакомления и домашнего использования.",
"about-goToPro": "Перейти на ГоNets-PRO",
"about-homepage": "Домашняя страница",
"about-documentation": "Документация",
"about-license": "Лицензия",
"about-copyright": "Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.",
"main-logoAlt": "Логотип приложения",
"main-networkSettings": "Настройки сети",
"main-about": "О программе",
"nodes-availableNodes": "Доступные узлы",
"nodes-rootAddress": "Корневой адрес",
"nodes-name": "Имя",
"nodes-loading": "Загрузка",
"nodes-additionalNodes": "Дополнительные узлы",
"nodes-node": "Узел",
"nodes-address": "Адрес",
"nodes-connectionError": "Не удалось подключиться к серверу по адресу ",
"settings-nodesSection": "Узлы подсети",
"settings-intervalScanning": "Интервальное сканирование",
"settings-scanInterval": "Интервал сканирования узлов (мин)",
"settings-displayNames": "Отображаемые имена при отсутствии",
"settings-ipAddress": "IP адрес",
"settings-unknown": "Неизвестно",
"settings-rowsPerPage": "Количество строк в таблице",
"settings-displaySettings": "Отображение узлов",
"settings-configure": "Настроить",
"settings-clearSessionData": "Очистить данные сессии",
"settings-clear": "Очистить",
"settings-clearFailed": "Данные не были удалены: ",
"settings-exportData": "Экспорт данных узлов",
"settings-export": "Экспортировать",
"settings-saveNodesTitle": "Сохранить данные узлов",
"settings-exportSuccess": "Данные успешно экспортированы!",
"settings-exportError": "Ошибка экспорта: ",
"settings-status": "Онлайн",
"settings-name": "Имя",
"settings-vendor": "Производитель",
"settings-openPorts": "Открытые порты",
"settings-lastActivity": "Последняя активность",
"settings-note": "Заметка",
"settings-action": "Действие",
"settings-title": "Настройки",
"settings-resetSettings": "Сбросить настройки",
"settings-resetConfirm": "Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?",
"settings-cancel": "Отменить",
"settings-save": "Сохранить",
"settings-fixErrors": "Исправьте ошибки перед сохранением",
"settings-fillRequired": "Заполните все обязательные поля",
"settings-dataNotDeleted": "Данные не были удалены: ",
"settings-saveNetworksTitle": "Сохранить данные сетей",
"settings-fieldRequired": "Поле не может быть пустым",
"settings-enterNumber": "Пожалуйста, введите число",
"settings-valueTooSmall": "Значение не может быть меньше ",
"settings-valueTooLarge": "Значение не может быть больше ",
"settings-sections-subnetNodes": "Узлы подсети",
"settings-sections-accessPoints": "Точки доступа",
"settings-sections-appearance": "Внешний вид",
"settings-exportNetworkData": "Экспорт данных точек доступа",
"settings-colorTheme": "Цветовая тема",
"settings-light": "Светлая",
"settings-dark": "Тёмная",
"settings-system": "Как в системе",
"settings-interfaceLanguage": "Язык интерфейса",
"settings-russian": "Русский",
"settings-english": "Английский"
}

View file

@ -1,77 +0,0 @@
import React from 'react';
import styled from "styled-components";
import {buttonNames} from "../../const/buttons/names";
import {IconComponent} from "../../const/buttons/icons";
const PaginateWrapper = styled.div`
width: 100%;
margin-top: 24px;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
`
const Page = styled.button<{ active?: boolean }>`
width: 36px;
height: 36px;
background: ${(props) => props.active ? props.theme.borderColor : props.theme.primary};
color: ${(props) => props.active ? props.theme.primary : props.theme.textColor};
border: 1px solid ${(props => props.theme.borderColor)};
font-size: 16px;
cursor: pointer;
border-radius: 4px;
transition: .4s ease;
&:hover {
background: ${(props) => props.active ? props.theme.textColor : props.theme.background};;
}
`
interface PaginationProps {
currentPage: number;
totalPages: number;
onPrevPage: () => void;
onNextPage: () => void;
onPageChange: (page: number) => void;
}
const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage, onPageChange}: PaginationProps) => {
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
return (
<PaginateWrapper>
<Page
onClick={onPrevPage}
disabled={currentPage === 1}
>
<IconComponent
width="20"
height="20"
name={buttonNames[17]}
/>
</Page>
{pageNumbers.map((page, i) => (
<Page
key={i}
onClick={() => onPageChange(page)}
active={currentPage === page}
>
{page}
</Page>
))}
<Page
onClick={onNextPage}
disabled={currentPage === totalPages}
>
<IconComponent
width="20"
height="20"
name={buttonNames[6]}
/>
</Page>
</PaginateWrapper>
);
};
export default Pagination;

View file

@ -1,143 +0,0 @@
import React, {useEffect, useState} from 'react';
import {ButtonContainer, ModalContainer, ModalContent, ModalTitle} from "../style";
import {MainButton} from "../../UI/buttons/mainButton";
import {ModalMap} from "../windowsType";
import {useModalContext} from "../../../store";
import styled from "styled-components";
import { useSettingsStore } from '../../../store/settings/settingsStore';
import {useTranslation} from "../../../utils/i18n/hooks";
const StyledTableList = styled.ul`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 0 40px;
list-style-type: none;
`
const StyledTableItem = styled.li`
width: 160px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background-color: ${props => props.theme.accent};
color: ${props => props.theme.primary};
border: 1px solid ${props => props.theme.primary};
&:first-child {
border-radius: 16px 0 0 16px;
}
&:last-child {
border-radius: 0 16px 16px 0;
}
&.off{
background-color: ${props => props.theme.offlineColor};
}
`
const CustomizeNodes = () => {
const { openModal } = useModalContext();
const pendingChanges = useSettingsStore(state => state.pendingChanges);
const updateSetting = useSettingsStore(state => state.updateSetting);
const getSetting = useSettingsStore(state => state.getSetting);
const initializeSettings = useSettingsStore(state => state.initializeSettings);
const {t} = useTranslation('common');
const [dragIndex, setDragIndex] = useState<number | null>(null);
// Получаем текущие значения из хранилища
const initialColumns = getSetting('table-columns-order').currentValue as string[];
const disabledColumns = getSetting('table-columns-disabled').currentValue as number[];
const items = pendingChanges['table-columns-order'] ?? initialColumns;
const disabledItems = pendingChanges['table-columns-disabled'] ?? disabledColumns;
// Drag & Drop
const handleDragStart = (index: number) => {
setDragIndex(index);
};
const handleDragOver = (index: number) => {
if (dragIndex === null || index === dragIndex) return;
const newItems = [...items];
const [movedItem] = newItems.splice(dragIndex, 1);
newItems.splice(index, 0, movedItem);
updateSetting('table-columns-order', newItems);
setDragIndex(index);
};
const handleDrop = () => {
setDragIndex(null);
};
// Переключение состояния
const handleSwitch = (index: number) => {
const newDisabledItems = disabledItems.includes(index)
? disabledItems.filter((i: number) => i !== index)
: [...disabledItems, index];
updateSetting('table-columns-disabled', newDisabledItems);
};
// Закрытие с сохранением
const handleSave = () => {
openModal(ModalMap['10']);
};
useEffect(() => {
initializeSettings();
}, [initializeSettings]);
const getItemText = (item: string) => {
if (item === 'Онлайн') return t('settings-status');
if (item === 'Имя') return t('settings-name');
if (item === 'Производитель') return t('settings-vendor');
if (item === 'IP адрес') return t('settings-ipAddress');
if (item === 'Открытые порты') return t('settings-openPorts');
if (item === 'Последняя активность') return t('settings-lastActivity');
if (item === 'Заметка') return t('settings-note');
if (item === 'Действие') return t('settings-action');
return item;
};
return (
<ModalContainer style={{ height: '35vh', width: '80vw'}}>
<ModalTitle>
<h3>{t('customizeTable-title')}</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', height: "100%", gap: '24px', padding: 0 }}>
<StyledTableList>
{items.map((item:string, index:number) => (
<StyledTableItem
key={index}
className={disabledItems.includes(index) ? 'off' : ''}
draggable
onDragStart={() => handleDragStart(index)}
onDragOver={(e) => {
e.preventDefault();
handleDragOver(index);
}}
onDragEnd={handleDrop}
onClick={() => handleSwitch(index)}
>
{getItemText(item)}
</StyledTableItem>
))}
</StyledTableList>
</ModalContent>
<ButtonContainer>
<MainButton
type="button"
size="small"
title={t('common-back')}
onClick={handleSave}
/>
</ButtonContainer>
</ModalContainer>
);
};
export default CustomizeNodes;

View file

@ -1,226 +0,0 @@
import {Setting} from "../../store/settings/settingsStore";
import {useState} from "react";
import styled, {useTheme} from "styled-components";
import {IconComponent} from "../../const/buttons/icons";
import {buttonNames} from "../../const/buttons/names";
import SettingSelect from "./SettingSelect";
import {MainButton} from "../UI/buttons/mainButton";
import React from 'react';
import {useModalContext} from "../../store";
import {ModalMap} from "../modalwindows/windowsType";
import {useTranslation} from "../../utils/i18n/hooks";
const SettingBlock = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
gap: 24px;
margin-bottom: 18px;
font-size: 18px;
width: 90%;
input{
font-size: 16px;
outline: none;
background-color: ${props => props.theme.primary};
color: ${props => props.theme.textColor};
border: 1px solid ${props => props.theme.borderColor};
padding: 6px 8px;
border-radius: 4px;
}
input[type="number"]{
width: 80px;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
`
const StyledCheckbox = styled.input.attrs({ type: 'checkbox' })`
width: 22px !important;
height: 22px !important;
appearance: none;
-webkit-appearance: none;
position: relative;
cursor: pointer;
overflow: hidden;
&::after {
content: "";
position: absolute;
width: 100%;
height: 100%;
background: ${props => props.theme.primary};
top: 0;
left: 0;
}
&:checked::after {
background: ${props => props.theme.accent};
}
`;
const LabelCheckbox = styled.label`
position: relative;
cursor: pointer;
padding: 0;
svg{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`
const InputWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: end;
gap: 6px;
`
const ErrorText = styled.div`
color: ${props => props.theme.errorColor};
font-size: 12px;
`;
interface SettingItemProps {
setting: Setting;
value: any;
onChange: (value: any) => void;
onValidationError?: (error: string | null) => void;
}
const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => {
const theme = useTheme();
const { t } = useTranslation('common');
const [error, setError] = useState<string | null>(null);
const { openModal } = useModalContext();
const handleButtonAction = () => {
if (setting.id === 'customize-nodes') {
openModal(ModalMap['11']);
} else if (setting.action) {
// Создаем обертку для действия с переводами
const actionWithTranslations = async () => {
try {
// Временно заменяем alert на версию с переводами
const originalAlert = window.alert;
window.alert = (message: string) => {
// Применяем переводы к сообщениям
let translatedMessage = message;
if (message.startsWith('settings-dataNotDeleted')) {
translatedMessage = t('settings-dataNotDeleted') + message.replace('settings-dataNotDeleted', '');
} else if (message === 'settings-exportSuccess') {
translatedMessage = t('settings-exportSuccess');
} else if (message.startsWith('settings-exportError')) {
translatedMessage = t('settings-exportError') + message.replace('settings-exportError', '');
}
originalAlert(translatedMessage);
};
if (setting.action) {
await setting.action();
}
// Восстанавливаем оригинальный alert
window.alert = originalAlert;
} catch (error) {
console.error('Action error:', error);
}
};
actionWithTranslations();
}
};
// Изменение числа
const handleNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
let newError: string | null = null;
if (inputValue.trim() === '') {
newError = t('settings-fieldRequired');
onChange(null);
}
else if (isNaN(Number(inputValue))) {
newError = t('settings-enterNumber');
}
else {
const numValue = Number(inputValue);
if (setting.min !== undefined && numValue < setting.min) {
newError = t('settings-valueTooSmall') + setting.min;
}
else if (setting.max !== undefined && numValue > setting.max) {
newError = t('settings-valueTooLarge') + setting.max;
}
if (!newError) {
onChange(numValue);
}
}
setError(newError);
onValidationError?.(newError);
};
return (
<SettingBlock>
<label>{setting.label}</label>
{setting.type === 'boolean' && (
<LabelCheckbox>
<StyledCheckbox
checked={!!value}
onChange={(e) => onChange(e.target.checked)}
/>
<IconComponent name={buttonNames[19]} width="18" height="18" setColor={theme.primary} />
</LabelCheckbox>
)}
{setting.type === 'select' && setting.options && (
<SettingSelect
value={value}
options={setting.options}
onChange={(value) => onChange(value)}
/>
)}
{setting.type === 'text' && (
<input
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
)}
{setting.type === 'number' && (
<InputWrapper>
<input
type="number"
min={setting?.min}
max={setting?.max}
value={value === undefined || value === null ? '' : value}
onChange={handleNumberChange}
onKeyDown={(e) => {
if (!/[0-9]/.test(e.key) &&
!['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
e.preventDefault();
}
}}
className={error ? 'error' : ''}
/>
{error && <ErrorText>{error}</ErrorText>}
</InputWrapper>
)}
{setting.type === 'button' && (
<MainButton
type="button"
size="small"
title={setting.defaultValue || ''}
onClick={handleButtonAction}
/>
)}
{setting.type === 'hidden' && null}
</SettingBlock>
);
});
export { SettingItem };

View file

@ -1,118 +0,0 @@
import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { IconComponent } from '../../const/buttons/icons';
import {buttonNames} from "../../const/buttons/names";
interface SettingSelectProps {
value: any;
options: { value: any; label: string }[];
onChange: (value: any) => void;
disabled?: boolean;
}
const SelectWrapper = styled.div`
position: relative;
width: 100%;
max-width: 180px;
`;
const SelectHeader = styled.div<{ $isOpen: boolean; $disabled?: boolean }>`
padding: 6px 8px;
border: 1px solid ${props => props.theme.borderColor};
border-radius: 4px;
background-color: ${props => props.theme.primary};
cursor: ${props => props.$disabled ? 'not-allowed' : 'pointer'};
display: flex;
justify-content: space-between;
align-items: center;
opacity: ${props => props.$disabled ? 0.6 : 1};
font-size: 16px;
`;
const SelectOptions = styled.div<{ $isOpen: boolean }>`
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
max-height: 200px;
overflow-y: auto;
background: ${props => props.theme.primary};
border: 1px solid ${props => props.theme.borderColor || '#ccc'};
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
z-index: 10;
display: ${props => props.$isOpen ? 'block' : 'none'};
`;
const OptionItem = styled.div<{ $isSelected: boolean }>`
padding: 6px 8px;
cursor: pointer;
background-color: ${props => props.$isSelected ? (props.theme.accent) : 'transparent'};
color: ${props => props.$isSelected ? (props.theme.primary) : 'inherit'};
`;
const ArrowIcon = styled.span<{ $isOpen: boolean }>`
transition: transform 0.2s;
transform: rotate(${props => props.$isOpen ? '90deg' : '0deg'});
display: flex;
align-items: center;
`;
const SettingSelect: React.FC<SettingSelectProps> = ({value, options, onChange, disabled = false
}) => {
const [isOpen, setIsOpen] = useState(false);
const selectRef = useRef<HTMLDivElement>(null);
const selectedOption = options.find(opt => opt.value === value);
const toggleDropdown = () => {
if (!disabled) {
setIsOpen(!isOpen);
}
};
const handleSelect = (optionValue: any) => {
onChange(optionValue);
setIsOpen(false);
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (selectRef.current && !selectRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<SelectWrapper ref={selectRef}>
<SelectHeader
$isOpen={isOpen}
onClick={toggleDropdown}
$disabled={disabled}
>
<span>{selectedOption?.label || 'Select...'}</span>
<ArrowIcon $isOpen={isOpen}>
<IconComponent name={buttonNames[6]} width="16" height="16" />
</ArrowIcon>
</SelectHeader>
<SelectOptions $isOpen={isOpen}>
{options.map((option) => (
<OptionItem
key={option.value}
$isSelected={value === option.value}
onClick={() => handleSelect(option.value)}
>
{option.label}
</OptionItem>
))}
</SelectOptions>
</SelectWrapper>
);
};
export default SettingSelect;

View file

@ -1,264 +0,0 @@
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../modalwindows/style";
import { MainButton } from "../UI/buttons/mainButton";
import {useCallback, useEffect, useMemo, useState} from "react";
import styled, {useTheme} from "styled-components";
import {Setting, useSettingsStore} from "../../store/settings/settingsStore";
import {SettingItem} from "./SettingItem";
import {IconComponent} from "../../const/buttons/icons";
import {buttonNames} from "../../const/buttons/names";
import {useTranslation} from "../../utils/i18n/hooks";
interface Props {
onClose: () => void;
}
const SectionsSidebar = styled.ul`
display: flex;
justify-content: start;
align-items: start;
flex-direction: column;
gap: 4px;
width: 20%;
min-width: 180px;
list-style-type: none;
padding: 0;
margin: 0;
`
const SectionsBTN = styled.li`
font-size: 18px;
border: none;
cursor: pointer;
width: 100%;
padding:10px 16px;
box-sizing: border-box;
border-radius: 0 12px 12px 0;
&.active{
background-color: ${(props) => props.theme.accent};
color: ${props => props.theme.primary};
}
`
const SectionActive = styled.div`
width: 80%;
`
const SectionLine = styled.span`
height: 100%;
width: 1px;
background-color: #9D9D9D;
`
const CloseBtn = styled.span`
cursor: pointer;
`
const SettingsButtons = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
span{
display: flex;
gap: 12px;
}
`
export const Settings = ({ onClose }: Props) => {
const settingsConfig = useSettingsStore(state => state.settingsConfig);
const settingsValues = useSettingsStore(state => state.settingsValues);
const activeSection = useSettingsStore(state => state.activeSection);
const pendingChanges = useSettingsStore(state => state.pendingChanges);
const setActiveSection = useSettingsStore(state => state.setActiveSection);
const updateSetting = useSettingsStore(state => state.updateSetting);
const applyChanges = useSettingsStore(state => state.applyChanges);
const discardChanges = useSettingsStore(state => state.discardChanges);
const getSetting = useSettingsStore(state => state.getSetting);
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
const initializeSettings = useSettingsStore(state => state.initializeSettings);
const updateSettingsConfig = useSettingsStore(state => state.updateSettingsConfig);
const theme = useTheme()
const { t, language } = useTranslation('common');
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
const [currentLanguage, setCurrentLanguage] = useState(language);
// Проверка действий
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
setFieldErrors(prev => {
const newErrors = { ...prev };
if (error) {
newErrors[fieldId] = error;
} else {
delete newErrors[fieldId];
}
return newErrors;
});
}, []);
const settingsBySection = useMemo(() => {
return settingsConfig.reduce((acc, setting) => {
// Применяем переводы к настройкам
const translatedSetting = {
...setting,
sectionId: t(setting.sectionId),
label: t(setting.label),
defaultValue: typeof setting.defaultValue === 'string' && setting.defaultValue.startsWith('settings-') ? t(setting.defaultValue) : setting.defaultValue,
options: setting.options ? setting.options.map((option: any) => ({
...option,
label: option.label.startsWith('settings-') ? t(option.label) : option.label
})) : setting.options,
value: settingsValues[setting.id] ?? setting.defaultValue
};
if (!acc[translatedSetting.sectionId]) {
acc[translatedSetting.sectionId] = [];
}
acc[translatedSetting.sectionId].push(translatedSetting);
return acc;
}, {} as Record<string, Setting[]>);
}, [settingsConfig, settingsValues, t]);
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
useEffect(() => {
updateSettingsConfig();
initializeSettings();
}, [updateSettingsConfig, initializeSettings]);
useEffect(() => {
if (sections.length > 0 && !activeSection) {
setActiveSection(sections[0]);
}
}, [sections, activeSection, setActiveSection]);
// Обновляем текущий язык при изменении
useEffect(() => {
setCurrentLanguage(language);
}, [language]);
const handleSave = useCallback(() => {
const hasErrors = Object.values(fieldErrors).some(error => error?.trim());
if (hasErrors) {
alert(t('settings-fixErrors'));
return;
}
const requiredFields = settingsConfig.filter(s => s.required);
const emptyRequiredFields = requiredFields.filter(f => {
const value = pendingChanges[f.id] ?? settingsValues[f.id] ?? f.defaultValue;
return value == null || value === '';
});
if (emptyRequiredFields.length > 0) {
alert(t('settings-fillRequired'));
return;
}
// Проверяем, изменился ли язык
const newLanguage = pendingChanges['language'] ?? settingsValues['language'];
const languageChanged = newLanguage && newLanguage !== currentLanguage;
applyChanges();
onClose();
// Если язык изменился, перезагружаем приложение
if (languageChanged) {
setTimeout(() => {
window.location.reload();
}, 100);
}
}, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose, t, currentLanguage]);
const handleCancel = useCallback(() => {
discardChanges();
setFieldErrors({});
onClose();
}, [discardChanges, onClose]);
const handleReset = () => {
if (window.confirm(t('settings-resetConfirm'))) {
resetToDefaults();
}
};
// Обработка нажатий клавиш
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter')
handleSave();
if (e.key === 'Escape')
handleCancel()
};
document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, [handleSave, handleCancel]);
return (
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
<ModalTitle>
<h3>{t('settings-title')}</h3>
<CloseBtn>
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
</CloseBtn>
</ModalTitle>
<ModalContent style={{ display: 'flex', height:"100%", gap: '24px', padding:0 }}>
<SectionsSidebar>
{sections.map((sectionId) => (
<SectionsBTN
key={sectionId}
className={activeSection === sectionId ? 'active' : ''}
onClick={()=>setActiveSection(sectionId)}
>
{sectionId}
</SectionsBTN>
))}
</SectionsSidebar>
<SectionLine/>
<SectionActive>
{activeSection && settingsBySection[activeSection] && (
<>
{settingsBySection[activeSection].map((setting) => (
<SettingItem
key={setting.id}
setting={setting}
value={getSetting(setting.id).currentValue}
onValidationError={(error) => handleValidationError(setting.id, error)}
onChange={(value) => updateSetting(setting.id, value)}
/>
))}
</>
)}
</SectionActive>
</ModalContent>
<ButtonContainer>
<SettingsButtons>
<MainButton
type="button"
size="small"
title={t('settings-resetSettings')}
onClick={handleReset}
color={theme.offlineColor}
hoverColor={theme.borderColor}
/>
<span>
<MainButton
type="button"
size="small"
title={t('settings-cancel')}
onClick={handleCancel}
color={theme.offlineColor}
hoverColor={theme.borderColor}
/>
<MainButton
type="button"
size="small"
title={t('settings-save')}
onClick={handleSave}
/>
</span>
</SettingsButtons>
</ButtonContainer>
</ModalContainer>
);
};

View file

@ -1,252 +0,0 @@
import { MainButton } from "../../../UI/buttons/mainButton"
import { workSpaceNames } from "../../../../const/workspace/names"
import { useWorkSpaceStore } from "../../../../store"
import styled, {useTheme} from "styled-components"
import ThemeButton from "../../../UI/buttons/toggletheme"
import {buttonNames} from "../../../../const/buttons/names";
import {IconComponent} from "../../../../const/buttons/icons";
import {useTranslation} from "../../../../utils/i18n/hooks";
type Props = {
from: 'reg' | 'helloWindow' | 'workSpace'
onBack?: () => void
}
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
background: ${(props => props.theme.background)};
padding: 50px;
box-sizing: border-box;
`
const Content = styled.div`
max-width: 1200px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
box-sizing: border-box;
padding: 40px 50px;
background: ${(props => props.theme.primary)};
border-radius: 18px;
position: relative;
`
const Listing = styled.div`
display: flex;
flex-direction: column;
background-color: ${(props => props.theme.secondary)};
border-radius: 10px;
padding: 24px 20px;
p{
font-weight: bold;
font-size: 24px;
}
`
const List = styled.ul`
display: flex;
flex-direction: column;
margin: 10px 0;
padding: 0;
list-style-type: none;
font-size: 18px;
gap: 4px;
& li:before {
color: ${(props => props.theme.accent)};
content: "•";
font-size: 28px;
width: .8em;
display: inline-block;
position: relative;
top: 2px;
}
`
const Title = styled.div`
font-size: 20px;
`
const SubTitle = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
background-color:rgba(255, 248, 225, 0.6);
border-left: #ff9800 solid 4px;
border-radius: 0 8px 8px 0;
padding: 20px 12px;
a{
text-decoration: none;
color: ${(props => props.theme.errorColor)};
font-weight: 500;
font-size: 18px;
width: max-content;
}
p{
font-size: 18px;
color: ${(props=>props.theme.textColor)};
}
.icon{
width: 32px;
height: 32px;
fill: #ff9800;
}
`
const Links = styled.div`
display: flex;
justify-content: start;
gap: 20px;
`
const LinkBlock = styled.a`
text-decoration: none;
color: ${(props => props.theme.accent)};;
`
const Copyright = styled.div`
width: 100%;
text-align: center;
color: ${(props => props.theme.offlineColor)};
`
const ButtonContainer = styled.div`
position: absolute;
top: 12px;
right: 14px;
`
const AboutLine = styled.hr`
border: ${(props => props.theme.hoverBtn)} 1px solid;
width: 100%;
`
const MainTitle = styled.div`
width: 100%;
display: flex;
gap: 15px;
align-items: center;
justify-content: space-between;
padding-top: 10px;
box-sizing: border-box;
img{
width: 50px;
height: 50px;
}
.version{
text-wrap: nowrap;
background-color: ${(props => props.theme.hoverBtn)};
color: ${(props => props.theme.borderColor)};
border-radius: 20px;
font-size: 18px;
padding: 10px 18px;
line-height: 1;
}
.title{
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
}
`
export default function About({ from, onBack }: Props) {
const { setWorkModule } = useWorkSpaceStore()
const {t} = useTranslation('common')
const getBackHandler = () => {
switch (from) {
case 'reg':
return onBack
case 'helloWindow':
return () => setWorkModule(workSpaceNames[1])
case 'workSpace':
return () => setWorkModule(workSpaceNames[3])
default:
return undefined
}
}
const backHandler = getBackHandler()
const showBackButton = from !== 'reg' || (from === 'reg' && onBack)
return (
<Wrapper>
<Content>
<MainTitle>
<div className={'title'}>
<img src="./assets/images/gossys.png" alt={t('about-logoAlt')} />
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}>
<h1>{t('about-title')}</h1>
</div>
</div>
<p className={'version'}>v 0.0.3</p>
</MainTitle>
<AboutLine/>
<Title>
<p>{t('about-description')}</p>
</Title>
<Listing>
<p>{t('about-featuresTitle')}:</p>
<List>
<li>{t('about-feature1')}</li>
<li>{t('about-feature2')}</li>
<li>{t('about-feature3')}</li>
</List>
</Listing>
<SubTitle>
<svg className="icon" viewBox="0 0 24 24">
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
</svg>
<p>{t('about-disclaimer')}</p>
<a href={'https://gossys.dev/pages/projects/gonets'}>
{t('about-goToPro')}
</a>
</SubTitle>
<Links>
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
{t('about-homepage')}
</LinkBlock>
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
{t('about-documentation')}
</LinkBlock>
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
{t('about-license')}
</LinkBlock>
</Links>
<Copyright>
{t('about-copyright')}
</Copyright>
<ButtonContainer>
{showBackButton && backHandler && (
<IconComponent
width="36"
height="36"
name={buttonNames[12]}
onClick={backHandler}
/>
)}
{from == 'reg' &&
<ThemeButton />
}
</ButtonContainer>
</Content>
</Wrapper>
)
}

View file

@ -1,85 +0,0 @@
import React, {useState} from 'react';
import styled, {keyframes} from 'styled-components';
import {useTranslation} from "../../../../../../utils/i18n/hooks";
interface Props {
value: string;
setIpScanning: (value: string) => void;
loading: boolean;
isValid: boolean;
setIsValid: (value: boolean) => void;
}
const Input = styled.input`
height: 40px;
width: 210px;
font-size: 20px;
text-align: center;
background-color: transparent;
border: none;
outline: none;
border-bottom: 2px solid ${props => props.theme.offlineColor};
color: ${props => props.theme.textColor};
transition: all 0.3s ease;
&:focus {
border-bottom: 2px solid ${props => props.theme.accent};
width: 270px;
}
&::placeholder {
color: ${props => props.theme.offlineColor};
transition: all 0.3s ease;
}
&:focus::placeholder {
opacity: 0;
}
&:disabled {
opacity: 0;
}
`
const errorVisible = keyframes`
0%{opacity: 0}
100%{opacity: 1}
`;
const ErrorMessage = styled.span`
margin-top: 6px;
color: ${props=>props.theme.errorColor};
animation: ${errorVisible} .4s linear;
`
const IpScanning = ({value, setIpScanning, loading, isValid, setIsValid}: Props) => {
const {t} = useTranslation('common')
const validateInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
const allowedKeys = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'.', '/',
'Backspace', 'Delete',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
];
if (!allowedKeys.includes(e.key)) {
e.preventDefault();
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIpScanning(e.target.value);
}
return (
<>
<Input
value={value}
onChange={handleChange}
placeholder={t('ipInput-placeholder')}
onKeyDown={validateInput}
disabled={loading}
/>
{!isValid && !loading && (
<ErrorMessage>{t('ipInput-error')}</ErrorMessage>
)}
</>
);
};
export default IpScanning;

View file

@ -1,115 +0,0 @@
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
import {DeviceUser} from "../../../../../../store/useDeviceStore";
import {usePaginateSetting} from "../../../../../../hooks/useSettings";
import Pagination from "../../../../../UI/Pagination";
import {useTranslation} from "../../../../../../utils/i18n/hooks";
const ScanBtn = styled.button`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
transition: .4s ease;
&:hover{
transform: scale(1.03);
}
`
interface ListUsersProps {
loading: boolean;
handleScan: ()=>void;
devices: DeviceUser[];
scanInitiated: boolean;
}
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
const {t} = useTranslation('common')
const activeDevices = devices.filter(device => device.status === 'online');
const inactiveDevices = devices.filter(device => device.status !== 'online');
// Пагинация
const paginateSetting = usePaginateSetting();
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
useEffect(() => {
setDevicesPerPage(paginateSetting);
}, [paginateSetting]);
const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
const currentActiveDevices = activeDevices.slice(indexOfFirstDevice, indexOfLastDevice);
const currentInactiveDevices = inactiveDevices.slice(indexOfFirstDevice, indexOfLastDevice);
const totalPages = Math.ceil(devices.length / devicesPerPage);
const handlePrevPage = () => {
if(currentPage > 1) setCurrentPage(currentPage - 1);
}
const handleNextPage = () => {
if(currentPage < totalPages) setCurrentPage(currentPage + 1);
}
const onPageChange = (page: number) => {
setCurrentPage(page);
}
return (
<>
<ScanBtn onClick={handleScan} disabled={loading}>
{loading ? t('userScanner-loading') : devices.length > 0 ? t('userScanner-refresh') : t('userScanner-scan')}
</ScanBtn>
{devices.length > 0 ? (
<div style={{
display: 'flex',
width: '70%',
gap: '20px',
}}>
{/* Колонка активных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>
{t('listUsers-activeNodes')} ({activeDevices.length})
</h3>
{currentActiveDevices.map((device, index) => (
<DeviceCard
key={index}
status="online"
device={device}
/>
))}
</div>
{/* Колонка неактивных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>
{t('listUsers-inactiveNodes')} ({inactiveDevices.length})
</h3>
{currentInactiveDevices.map((device, index) => (
<DeviceCard
key={index}
status="offline"
device={device}
/>
))}
</div>
</div>
) : scanInitiated && !loading ? (
<p style={{ marginTop: '20px' }}>{t('userScanner-notFound')}</p>
) : null}
{totalPages > 1 && (
<Pagination
onPageChange={onPageChange}
currentPage={currentPage}
totalPages={totalPages}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
)}
</>
);
};
export default ListUsers;

View file

@ -1,268 +0,0 @@
import React, {useEffect, useMemo, useState} from 'react';
import { DeviceUser } from '../../../../../../store/useDeviceStore';
import styled from 'styled-components';
import {IconComponent} from "../../../../../../const/buttons/icons";
import {buttonNames} from "../../../../../../const/buttons/names";
import {ModalMap} from "../../../../../modalwindows/windowsType";
import {useModalContext} from "../../../../../../store";
import {useTranslation} from "../../../../../../utils/i18n/hooks";
import Pagination from "../../../../../UI/Pagination";
import {
useDisabledColumns,
useNameSetting,
useOrdersColumns,
usePaginateSetting
} from "../../../../../../hooks/useSettings";
interface TableScannerProps {
devices: DeviceUser[];
}
const TableContainer = styled.div`
width: 100%;
margin: 24px 0;
`;
const StyledTable = styled.table`
width: 100%;
border-collapse: separate;
border-spacing: 0;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
background: ${({ theme }) => theme.primary};
`;
const TableHeaderCell = styled.th<{ sortable?: boolean }>`
padding: 16px 24px;
text-align: left;
font-weight: 600;
font-size: 14px;
color: ${({ theme }) => theme.textColor};
background: ${({ theme }) => theme.primary};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
`;
const TableRow = styled.tr<{ clickable?: boolean }>`
transition: all 0.2s ease;
&:hover {
background-color: ${({ theme }) => theme.background};
}
`;
const TableCell = styled.td`
padding: 16px 24px;
font-size: 14px;
color: ${({ theme }) => theme.text};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
button{
background-color: transparent;
border: none;
cursor: pointer;
}
`;
const StatusBadge = styled.span<{ status:string }>`
width: 10px;
height: 30px;
background-color: ${({theme, status}) => (status==='online' ? theme.successColor : theme.offlineColor)};
display: inline-block;
border-radius:4px;
`
export const TableScanner = ({ devices }: TableScannerProps) => {
const { openModal } = useModalContext();
const nameSetting = useNameSetting();
const ordersColumns = useOrdersColumns();
const disabledColumns = useDisabledColumns();
const paginateSetting = usePaginateSetting();
const {t} = useTranslation('common');
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
console.log(ordersColumns);
useEffect(() => {
setDevicesPerPage(paginateSetting);
}, [paginateSetting]);
const openModalNote = (e: React.MouseEvent, device: DeviceUser) => {
e.stopPropagation();
openModal(ModalMap['6'], {
address: device.mac,
currentNote: device.note || ''
});
};
const defaultOrder = useMemo(() => ([
t('settings-status'),
t('settings-name'),
t('settings-vendor'),
t('settings-ipAddress'),
t('settings-openPorts'),
t('settings-lastActivity'),
t('settings-note'),
t('settings-action'),
]), [t]);
const normalizedOrders = useMemo(
() => (Array.isArray(ordersColumns) && ordersColumns.length ? ordersColumns : defaultOrder),
[ordersColumns, defaultOrder]
);
const normalizedDisabled = useMemo(
() => (Array.isArray(disabledColumns) ? disabledColumns : []),
[disabledColumns]
);
const visibleColumns = useMemo(() => {
const normalizeColumnName = (column: string) => column?.toLowerCase();
const columnToKey = (column: string) => {
const col = normalizeColumnName(column);
switch (col) {
case 'settings-status':
case normalizeColumnName(t('settings-status')):
case 'онлайн':
case 'status':
return 'status';
case 'settings-name':
case normalizeColumnName(t('settings-name')):
case 'имя':
case 'name':
return 'name';
case 'settings-vendor':
case normalizeColumnName(t('settings-vendor')):
case 'производитель':
case 'vendor':
return 'vendor';
case 'settings-ipaddress':
case normalizeColumnName(t('settings-ipAddress')):
case 'ip адрес':
case 'ip address':
case 'ip':
return 'ip';
case 'settings-openports':
case normalizeColumnName(t('settings-openPorts')):
case 'открытые порты':
case 'open ports':
case 'ports':
return 'ports';
case 'settings-lastactivity':
case normalizeColumnName(t('settings-lastActivity')):
case 'последняя активность':
case 'last activity':
case 'activity':
return 'activity';
case 'settings-note':
case normalizeColumnName(t('settings-note')):
case 'заметка':
case 'note':
return 'note';
case 'settings-action':
case normalizeColumnName(t('settings-action')):
case 'действие':
case 'action':
return 'action';
default:
return '';
}
};
return normalizedOrders
.map((column, index) => ({ column, index }))
.filter(({ index }) => !normalizedDisabled.includes(index))
.map(({ column }) => columnToKey(column))
.filter(Boolean);
}, [normalizedOrders, normalizedDisabled, t]);
// Пагинация
const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
const currentDevices = devices.slice(indexOfFirstDevice, indexOfLastDevice);
const totalPages = Math.ceil(devices.length / devicesPerPage);
const handlePrevPage = () => {
if (currentPage > 1) setCurrentPage(currentPage - 1);
};
const handleNextPage = () => {
if (currentPage < totalPages) setCurrentPage(currentPage + 1);
};
const onPageChange = (page: number) => {
setCurrentPage(page);
};
// Рендер ячейки таблицы
const renderTableCell = (column: string, item: DeviceUser) => {
switch (column) {
case 'status':
return <TableCell key="status"><StatusBadge status={item.status} /></TableCell>;
case 'name':
return <TableCell key="name">{item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')}</TableCell>;
case 'vendor':
return <TableCell key="vendor">{item.vendor}</TableCell>;
case 'ip':
return <TableCell key="ip">{item.ip}</TableCell>;
case 'ports':
return <TableCell key="ports">{item.open_ports.join(', ') || '-'}</TableCell>;
case 'activity':
return <TableCell key="activity">{item.last_seen}</TableCell>;
case 'note':
return <TableCell key="note">{item.note || '-'}</TableCell>;
case 'action':
return (
<TableCell key="action">
<button onClick={(e) => openModalNote(e, item)}>
<IconComponent name={buttonNames[16]} width="26" height="26" />
</button>
</TableCell>
);
default:
return null;
}
};
return (
<TableContainer>
<StyledTable>
<thead>
<tr>
{visibleColumns.map(column => {
switch (column) {
case 'status': return <TableHeaderCell key="status"></TableHeaderCell>;
case 'name': return <TableHeaderCell key="name">{t('settings-name')}</TableHeaderCell>;
case 'vendor': return <TableHeaderCell key="vendor">{t('settings-vendor')}</TableHeaderCell>;
case 'ip': return <TableHeaderCell key="ip">{t('settings-ipAddress')}</TableHeaderCell>;
case 'ports': return <TableHeaderCell key="ports">{t('settings-openPorts')}</TableHeaderCell>;
case 'activity': return <TableHeaderCell key="activity">{t('settings-lastActivity')}</TableHeaderCell>;
case 'note': return <TableHeaderCell key="note">{t('settings-note')}</TableHeaderCell>;
case 'action': return <TableHeaderCell key="action"></TableHeaderCell>;
default: return null;
}
})}
</tr>
</thead>
<tbody>
{currentDevices.map((item, index) => (
<TableRow key={index}>
{visibleColumns.map(column => renderTableCell(column, item))}
</TableRow>
))}
</tbody>
</StyledTable>
{totalPages > 1 && (
<Pagination
onPageChange={onPageChange}
currentPage={currentPage}
totalPages={totalPages}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
)}
</TableContainer>
);
};

View file

@ -1,9 +0,0 @@
interface Window {
electron: {
quitApp: () => void;
};
electronAPI: {
showSaveDialog: (options: Electron.SaveDialogOptions) => Promise<Electron.SaveDialogReturnValue>;
writeFile: (path: string, data: string) => Promise<{ success: boolean }>;
};
}

View file

@ -1,37 +0,0 @@
import {useSettingsStore} from "../store/settings/settingsStore";
import {defaultLanguage, Language} from "../utils/i18n/config";
export const useNameSetting = () => {
return useSettingsStore(state => state.getSettingValue('netnames-nodes')) as string;
};
export const usePaginateSetting = () => {
return useSettingsStore(state => state.getNumberSetting('paginate-nodes', 20));
};
export const useThemeSettings = () => {
return useSettingsStore(state => state.getSettingValue('theme'));
};
//Интервальное сканирование под узлов
export const useIntervalBoolean = () => {
return useSettingsStore(state => state.getSettingValue('rescan-nodes')) as boolean;
};
export const useIntervalTime = () => {
return useSettingsStore(state => state.getNumberSetting('interval-nodes', 60));
};
// Кастомизация таблиц
export const useOrdersColumns = () => {
return useSettingsStore(state => state.getSettingValue('table-columns-order')) as string[];
};
export const useDisabledColumns = () => {
return useSettingsStore(state => state.getSettingValue('table-columns-disabled')) as number[];
};
export const useLanguage = () => {
return useSettingsStore(
state => state.getSettingValue<Language>('language')
) ?? defaultLanguage;
};

View file

@ -1,30 +0,0 @@
const createAppearanceSettings = () => {
return [
{
id: 'theme',
sectionId: 'settings-sections-appearance',
label: 'settings-colorTheme',
type: 'select',
defaultValue: 'light',
options: [
{ value: 'light', label: 'settings-light' },
{ value: 'dark', label: 'settings-dark' },
{ value: 'system', label: 'settings-system' }
],
},
{
id: 'language',
sectionId: 'settings-sections-appearance',
label: 'settings-interfaceLanguage',
type: 'select',
defaultValue: 'ru',
options: [
{ value: 'ru', label: 'settings-russian' },
{ value: 'en', label: 'settings-english' },
],
},
] as const;
};
export { createAppearanceSettings };

View file

@ -1,55 +0,0 @@
import {useIpStore} from "../../ipStore";
import {NetworkService} from "../../../api/request";
const createNetworkSettings = () => {
const { ipAddress } = useIpStore.getState();
return [
{
id: 'clean-network',
type: 'button',
label: 'settings-clearSessionData',
sectionId: 'settings-sections-accessPoints',
defaultValue: 'settings-clear',
action: async ()=>{
try {
const res = await NetworkService.clearNetwork(ipAddress);
console.log(res);
}catch (e){
console.error(e);
alert('settings-dataNotDeleted' + e)
}
},
},
{
id: 'export-network',
type: 'button',
label: 'settings-exportNetworkData',
sectionId: 'settings-sections-accessPoints',
defaultValue: 'settings-export',
action: async () => {
try {
const response = await NetworkService.exportNetwork(ipAddress);
const { filePath } = await window.electronAPI.showSaveDialog({
title: 'settings-saveNetworksTitle',
defaultPath: "network_export.json",
filters: [{ name: "JSON", extensions: ["json"] }],
});
if (filePath) {
await window.electronAPI.writeFile(
filePath,
JSON.stringify(response.data, null, 2)
);
alert('settings-exportSuccess');
}
} catch (e: any) {
alert('settings-exportError' + e.message);
}
},
},
] as const;
};
export { createNetworkSettings };

View file

@ -1,119 +0,0 @@
import {useIpStore} from "../../ipStore";
import {NetworkService} from "../../../api/request";
const createNodesSettings = () => {
const { ipAddress } = useIpStore.getState();
return [
{
id: 'rescan-nodes',
sectionId: 'settings-sections-subnetNodes',
label: 'settings-intervalScanning',
type: 'boolean',
defaultValue: false,
},
{
id: 'interval-nodes',
sectionId: 'settings-sections-subnetNodes',
label: 'settings-scanInterval',
type: 'number',
defaultValue: 60,
min: 1,
max: 2880
},
{
id: 'netnames-nodes',
sectionId: 'settings-sections-subnetNodes',
label: 'settings-displayNames',
type: 'select',
defaultValue: 'ip',
options: [
{ value: 'ip', label: 'settings-ipAddress' },
{ value: 'unknown', label: 'settings-unknown' },
],
},
{
id: 'paginate-nodes',
type: 'number',
label: 'settings-rowsPerPage',
sectionId: 'settings-sections-subnetNodes',
defaultValue: 20,
min: 1,
},
{
id: 'customize-nodes',
type: 'button',
label: 'settings-displaySettings',
sectionId: 'settings-sections-subnetNodes',
defaultValue: 'settings-configure',
},
{
id: 'clean-nodes',
type: 'button',
label: 'settings-clearSessionData',
sectionId: 'settings-sections-subnetNodes',
defaultValue: 'settings-clear',
action: async ()=>{
try {
const res = await NetworkService.clearSubnet(ipAddress);
console.log(res)
}catch (e){
console.error(e);
alert('settings-dataNotDeleted' + e)
}
},
},
{
id: "export-nodes",
type: "button",
label: "settings-exportData",
sectionId: "settings-sections-subnetNodes",
defaultValue: "settings-export",
action: async () => {
try {
const response = await NetworkService.exportDevices(ipAddress);
const { filePath } = await window.electronAPI.showSaveDialog({
title: "settings-saveNodesTitle",
defaultPath: "nodes_export.json",
filters: [{ name: "JSON", extensions: ["json"] }],
});
if (filePath) {
await window.electronAPI.writeFile(
filePath,
JSON.stringify(response.data, null, 2)
);
alert("settings-exportSuccess");
}
} catch (e: any) {
alert("settings-exportError" + e.message);
}
},
},
{
id: 'table-columns-order',
type: 'hidden',
label: '',
sectionId: 'settings-sections-subnetNodes',
defaultValue: [
'settings-status',
'settings-name',
'settings-vendor',
'settings-ipAddress',
'settings-openPorts',
'settings-lastActivity',
'settings-note',
'settings-action'
]
},
{
id: 'table-columns-disabled',
label: '',
sectionId: 'settings-sections-subnetNodes',
type: 'hidden',
defaultValue: []
}
] as const;
};
export { createNodesSettings };

View file

@ -1,139 +0,0 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import {createNodesSettings} from "./config/nodesSettings";
import { createNetworkSettings } from './config/networkSettings';
import {createAppearanceSettings} from "./config/appearanceSettings";
type SettingValue = any;
type SettingsDictionary = Record<string, SettingValue>;
export type Setting = {
readonly id: string;
readonly type: 'boolean' | 'select' | 'text' | 'number' | 'button' | 'hidden';
readonly label: string;
readonly defaultValue: any;
value?: string;
readonly min?: number;
readonly max?: number;
readonly options?: any;
readonly sectionId: string;
readonly required?: boolean;
readonly action?: () => void;
};
type SettingsState = {
settingsConfig: Setting[];
settingsValues: SettingsDictionary;
activeSection: string | null;
pendingChanges: SettingsDictionary;
setActiveSection: (sectionId: string | null) => void;
updateSetting: (id: string, value: any) => void;
initializeSettings: () => void;
getSetting: (id: string) => Setting & { currentValue: any };
resetSettings: () => void;
applyChanges: () => void;
discardChanges: () => void;
resetToDefaults: () => void;
getSettingValue: <T = any>(id: string) => T;
getNumberSetting: (id: string, fallback?: number) => number;
updateSettingsConfig: () => void;
};
export const useSettingsStore = create<SettingsState>()(
persist(
(set, get) => ({
settingsConfig: [],
settingsValues: {},
activeSection: null,
pendingChanges: {},
setActiveSection: (sectionId) => set({ activeSection: sectionId }),
updateSetting: (id, value) =>
set((state) => ({
pendingChanges: { ...state.pendingChanges, [id]: value }
})),
resetToDefaults: () => set({
settingsValues: Object.fromEntries(
get().settingsConfig.map(setting => [
setting.id,
setting.defaultValue
])
),
pendingChanges: {}
}),
applyChanges: () =>
set((state) => ({
settingsValues: { ...state.settingsValues, ...state.pendingChanges },
pendingChanges: {}
})),
discardChanges: () => set({ pendingChanges: {} }),
initializeSettings: () => {
const { settingsValues, settingsConfig } = get();
const newValues = { ...settingsValues };
settingsConfig.forEach(setting => {
if (newValues[setting.id] === undefined) {
newValues[setting.id] = setting.defaultValue;
}
});
set({ settingsValues: newValues });
},
updateSettingsConfig: () => {
const defaultSettings: Setting[] = [
...createNodesSettings(),
...createNetworkSettings(),
...createAppearanceSettings(),
];
set({ settingsConfig: defaultSettings });
},
getSetting: (id) => {
const state = get();
const setting = state.settingsConfig.find(s => s.id === id);
if (!setting) throw new Error(`Setting ${id} not found`);
const hasPendingChange = Object.prototype.hasOwnProperty.call(state.pendingChanges, id);
const hasSavedValue = Object.prototype.hasOwnProperty.call(state.settingsValues, id);
const currentValue = hasPendingChange
? state.pendingChanges[id]
: hasSavedValue
? state.settingsValues[id]
: setting.defaultValue;
return {
...setting,
currentValue
};
},
getSettingValue: (id: string) => {
const state = get();
return state.settingsValues[id] ??
state.settingsConfig.find(s => s.id === id)?.defaultValue;
},
getNumberSetting: (id: string, fallback = 0) => {
const value = get().getSettingValue(id);
return Number(value) || fallback;
},
resetSettings: () => set({ settingsValues: {}, pendingChanges: {} }),
}),
{
name: 'app-settings',
partialize: (state) => ({
settingsValues: state.settingsValues
}),
onRehydrateStorage: () => (state) => {
state?.initializeSettings();
}
}
)
);

View file

@ -1,11 +0,0 @@
export const supportedLanguages = {
en: 'English',
ru: 'Русский',
} as const;
export type Language = keyof typeof supportedLanguages;
export const defaultLanguage = 'ru';
export const namespaces = ['common', 'components'] as const;
export type Namespace = (typeof namespaces)[number];

View file

@ -1,11 +0,0 @@
import { Namespace } from './config';
import { useI18n } from './provider';
export const useTranslation = (ns?: Namespace) => {
const { t, language } = useI18n();
return {
t: (key: string) => t(key, ns),
language,
};
};

View file

@ -1,92 +0,0 @@
'use client'
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { defaultLanguage, Language, Namespace, namespaces, supportedLanguages } from './config';
type Translation = Record<string, string>;
type NamespaceTranslations = Record<Namespace, Translation>;
type LanguageTranslations = Record<Language, NamespaceTranslations>;
const I18nContext = createContext<{
language: Language;
t: (key: string, namespace?: Namespace) => string;
}>({
language: defaultLanguage,
t: (key: string) => key,
});
interface I18nProviderProps {
children: React.ReactNode;
initialLanguage: Language;
}
export const I18nProvider = ({ children, initialLanguage }: I18nProviderProps) => {
const [language, setLanguage] = useState<Language>(initialLanguage);
const [translations, setTranslations] = useState<LanguageTranslations>({} as LanguageTranslations);
// Загрузка переводов
useEffect(() => {
const loadTranslations = async () => {
const newTranslations: LanguageTranslations = { ...translations };
await Promise.all(
(Object.keys(supportedLanguages) as Language[]).map(async (lang) => {
if (!newTranslations[lang]) {
newTranslations[lang] = {} as NamespaceTranslations;
await Promise.all(
namespaces.map(async (ns) => {
try {
const res = await fetch(`/locales/${lang}/${ns}.json`);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const data = await res.json();
newTranslations[lang][ns] = data;
} catch (e) {
console.error(`Failed to load ${lang}/${ns} translations`, e);
newTranslations[lang][ns] = {};
}
})
);
}
})
);
setTranslations(newTranslations);
};
loadTranslations();
}, []);
useEffect(() => {
setLanguage(initialLanguage);
}, [initialLanguage]);
const t = useMemo(() => {
return (key: string, namespace: Namespace = 'common') => {
return translations[language]?.[namespace]?.[key] || key;
};
}, [language, translations]);
const contextValue = useMemo(() => ({
language,
t,
}), [language, t]);
return (
<I18nContext.Provider value={contextValue}>
{children}
</I18nContext.Provider>
);
};
export const useI18n = () => {
const context = useContext(I18nContext);
if (!context) {
throw new Error('useI18n must be used within I18nProvider');
}
return context;
};

File diff suppressed because it is too large Load diff

View file

@ -1,72 +1,3 @@
<p align="center"><img src="assets/gonets_logo.svg" alt="Логотип ГоNets" width="15%"></p> # GoNets
<p align="center"><img src="assets/gonets_text.svg" alt="Написание ГоNets" width="70%"></p>
# ОГЛАВЛЕНИЕ
- [Ссылки](#cссылки:)
- [Что это?](#что-это?)
- [Почему MVP?](#почему-mvp?)
- [Текущий статус Приложения](#текущий-статус-приложения)
- [Стабильные версии:](#стабильные-версии:)
- [Правила наименования](#правила-наименования)
- [Коды компонентов](#коды-компонентов)
- [Нумерация версий](#нумерация-версий)
## Ссылки:
Здесь Вы найдете неполный список ссылок на официальные ресурсы:
- [WEB-страница приложения](https://gonets.gossys.dev)
- [Гибридная Офисная Система](https://hybryd.ru)
- [HELPDESK](https://helpdesk.gossys.dev/)
- [Раздел Базы знаний](https://helpdesk.gossys.dev/knowledgebase.php?category=17)
- [О Разработчике](https://gossys.dev/)
- [Документация](https://wiki.gossys.dev/docs/gonets)
## Что это?
**ГоNets** - это кроссплатформенное **MVP** - приложение для управления сетевыми настройками локального и удаленных Клиентов и проведения востребованных Пользователями и Администраторами сетевых операций.
Все это происходит с использованием нового, интуитивно понятного интерфейса, с полным интерактивным взаимодействием.
<p align="center"><img src="assets/work_scr.jpg" alt="Экран ГоNets" width="90%"></p>
## Почему MVP?
**ГоNets** - приложение, созданное для тестирования данного интерфейса, применительно к управлению настройками операционной системы. На основе Вашей реакции нами будут приняты решения о дальнейшей модернизации проекта, либо его полного перехода на более привычный интерфейс.
## Текущий статус Приложения
Текущий статус Приложения - В разработке. Чего еще ожидать от приложения, размещенного на Гит? Одновременно с этим - оно находится и в тестировании. Подробную информацию Вы можете получить в в разделах Задачи и Проекты.
### Стабильные версии:
- Версия для Deb-based: GNB-DEB64-0.0.3
- Версия для RPM-based: В разработке
- Версия для Windows: В разработке
- Версия для MacOS (Intel): В разработке
- Версия для MacOS (ARM): В разработке
## Правила наименования
Общий шаблон наименования - **CODE-ARCH-NUM-SPEC**, где:
- **CODE** - Код компонента (См. таблицу ниже);
- **ARCH** - Архитектура компонента в соответствии с международной классификацией;
- **NUM** - Номер версии, в соответствии с "Правилами нумерации версий";
- **SPEC** - Дополнительное буквенное обозначение в общепринятом формате. (Например RC, Beta, PreRelease)
### Коды компонентов
|Наименование|Описание|Код|
|---|---|---|
|**Project GoNets (Проект, Проект GoNets, GoNets, Программный комплекс, ПК)**| Программный комплекс вцелом.| ***GNP*** |
|**Server (Сервер)**| Программный компонент, отвечающий за получение и обработку данных, формирующий данные для последующего отображения|***GNS***|
|**Server Client (Серверный Клиент)**| Программный компонент, отвечающий за сбор данных с ПК или Сервера и передающий их для обработки|***GNK***|
|**Client (Клиент)** |Программный компонент, отвечающий за визуализацию и взаимодействие с Пользователем|***GNC***|
|**Bundle (Объединенное приложение)**| Версия приложения **ГоNets**, собранная из нескольких программных компонентов, объединенная одним инсталлятором для обспечения полного функционала|***GNB***|
### Нумерация версий
В проекте действует следующая система нумерации версий:
**XX.YY.ZZ** - где XX.YY.ZZ, обозначают:
- **ZZ**: текущий номер стабильного билда;
- **YY**: текущий номер минорной версии (Версии с полной обратной совместимостью с предыдущими, без кардинальных отличий в интерфейсе или функционале.)
- **XX**: Текущий номер мажорной версии (Версии с частичной или отсутствующей обратной совместимостью с предыдущими, либо с кардинальными отличиями в интерфейсе или функционале.)
Общий репозиторий для GoNets начиная с версии 0.0.1

3
SRV/.gitignore vendored
View file

@ -6,5 +6,4 @@ __pycache__
.wheels .wheels
.vendor .vendor
.python3-deb .python3-deb
storage/subnets/ storage/devices/
storage/wifi/

View file

@ -1,29 +1,5 @@
"""
Маршрутизация API-версии v1 для GONETS.
Модуль агрегирует и монтирует роутеры отдельных доменных областей под общим
корневым роутером. Каждый подключенный модуль предоставляет набор эндпоинтов
со своим префиксом и тегами для группировки в документации FastAPI.
Структура маршрутов:
- `/ufw` (UFW): управление правилами и состоянием Uncomplicated Firewall.
- `/ip` (IP): проверки и применение IP-конфигураций для сетевых профилей.
- `/network` (Network): получение и визуализация состояния интерфейсов,
управление позициями UI-контейнеров.
- `/auth` (Auth): регистрация, аутентификация (JWT), сессии.
- `/subnet` (Subnet): сканирование подсетей, заметки к устройствам,
взаимодействие с Wi-Fi (скан/подключение/отключение/статус).
- `WebSocket`: обработчики взаимодействия по WS (без префикса, путь внутри
модуля), периодическая трансляция сетевых данных.
- `/settings` (Settings): сервисные операции над локальными JSON-хранилищами
(очистка данных подсетей/wi-fi и пр.).
Назначение:
- Централизованная точка подключения доменных роутеров к приложению.
- Единообразие тегов и префиксов для автогенерации схемы и документации.
"""
from fastapi import APIRouter from fastapi import APIRouter
from .v1 import ufw, websocket, ip, network, auth, subnet, settings from .v1 import ufw, websocket, ip, network, auth, subnet
router = APIRouter() router = APIRouter()
@ -34,5 +10,3 @@ router.include_router(network.router, prefix="/network", tags=["Network"])
router.include_router(auth.router, prefix="/auth", tags=["Auth"]) router.include_router(auth.router, prefix="/auth", tags=["Auth"])
router.include_router(subnet.router, prefix="/subnet", tags=["Subnet"]) router.include_router(subnet.router, prefix="/subnet", tags=["Subnet"])
router.include_router(settings.router, prefix="/settings", tags=["Settings"])

View file

@ -1,10 +1,9 @@
# api/v1/auth.py
from fastapi import APIRouter, Depends, HTTPException, Request from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from services.auth_service import AuthService from services.auth_service import AuthService
from models.auth import UserCreate, UserLogin, UserResponse, Token from models.auth import UserCreate, UserLogin, UserResponse, Token
from storage.json_storage import JsonStorage from storage.json_storage import JsonStorage
from utils.i18n import get_translations, _
from babel.support import Translations
import logging import logging
import os import os
@ -15,24 +14,10 @@ router = APIRouter()
auth_service = AuthService() auth_service = AuthService()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
@router.post("/register", response_model=UserResponse) @router.post("/register", response_model=UserResponse)
async def register(user: UserCreate, translations: Translations = Depends(get_translations)): async def register(user: UserCreate):
"""Регистрирует нового пользователя. """Регистрирует нового пользователя"""
Создаёт нового пользователя на основе предоставленных данных и возвращает
информацию о зарегистрированном пользователе.
Args:
user (UserCreate): Объект с данными нового пользователя (username, password, is_admin).
translations: Объект переводов для текущего языка.
Returns:
UserResponse: Объект с данными зарегистрированного пользователя (id, username, is_admin).
Raises:
HTTPException: Если пользователь уже существует (status_code=400) или произошла
внутренняя ошибка (status_code=500).
"""
try: try:
db_user = auth_service.register(user) db_user = auth_service.register(user)
logger.info(f"Пользователь {user.username} успешно зарегистрирован") logger.info(f"Пользователь {user.username} успешно зарегистрирован")
@ -40,116 +25,69 @@ async def register(user: UserCreate, translations: Translations = Depends(get_tr
except HTTPException as e: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
logger.error(f"Error in register: {type(e).__name__} - {str(e)}") logger.error(f"Ошибка в register: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось зарегистрировать пользователя")) raise HTTPException(status_code=500,
detail="Не удалось зарегистрировать пользователя")
@router.post("/login", response_model=Token) @router.post("/login", response_model=Token)
async def login(user: UserLogin, request: Request, translations: Translations = Depends(get_translations)): async def login(user: UserLogin, request: Request):
"""Авторизует пользователя и создаёт сессию. """Авторизует пользователя и создаёт сессию"""
Проверяет учетные данные пользователя и возвращает токен и session_id для
последующей авторизации.
Args:
user (UserLogin): Объект с данными для входа (username, password).
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
Token: Объект с access_token и session_id для авторизации.
Raises:
HTTPException: Если учетные данные неверны (status_code=401) или произошла
внутренняя ошибка (status_code=500).
"""
try: try:
client_id = request.client.host client_id = request.client.host
logger.debug(f"Попытка входа в систему клиента {client_id}") logger.debug(f"Попытка авторизации для client_id {client_id}")
result = auth_service.login(user, client_id) result = auth_service.login(user, client_id)
logger.info(f"Успешная авторизация для клиента {client_id}, Идентификатор сессии: {result['session_id']}") logger.info(
f"Успешная авторизация для client_id {client_id}, session_id: {result['session_id']}"
)
return result return result
except HTTPException as e: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
logger.error(f"Ошибка при авторизации: {type(e).__name__} - {str(e)}") logger.error(f"Ошибка в login: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось авторизовать пользователя")) raise HTTPException(status_code=500,
detail="Не удалось авторизовать пользователя")
@router.get("/me") @router.get("/me")
async def get_current_user(request: Request, translations: Translations = Depends(get_translations)): async def get_current_user(request: Request):
"""Возвращает данные текущего пользователя и токен. """Возвращает данные текущего пользователя и токен"""
Проверяет активную сессию по client_id и возвращает информацию о пользователе
и токен для дальнейшего использования.
Args:
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключами "user" (UserResponse), "access_token" (str) и
"token_type" (str, всегда "bearer").
Raises:
HTTPException: Если сессия не найдена или истекла (status_code=401) или
произошла внутренняя ошибка (status_code=500).
"""
try: try:
client_id = request.client.host client_id = request.client.host
logger.debug(f"Запрос /me для клиента {client_id}") logger.debug(f"Запрос /me для client_id {client_id}")
result = auth_service.get_user_by_session(client_id) result = auth_service.get_user_by_session(client_id)
return { return {
"user": result["user"], "user": result["user"],
"access_token": result["access_token"], "access_token": result["access_token"],
"token_type": translations.gettext("bearer"), "token_type": "bearer",
} }
except HTTPException as e: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
logger.error(f"Ошибка при get_current_user: {type(e).__name__} - {str(e)}") logger.error(
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить данные пользователя")) f"Ошибка в get_current_user: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500,
detail="Не удалось получить данные пользователя")
@router.get("/logout") @router.get("/logout")
async def logout(request: Request, translations: Translations = Depends(get_translations)): async def logout(request: Request):
"""Выход из системы, удаление сессии. """Выход из системы, удаление сессии"""
Удаляет активную сессию пользователя по client_id.
Args:
request (Request): Объект запроса для получения client_id (IP-адрес клиента).
translations: Объект переводов для текущего языка.
Returns:
dict: Пустой словарь или сообщение об успешном выходе.
Raises:
HTTPException: Если сессия не найдена (status_code=401) или произошла
внутренняя ошибка (status_code=500).
"""
try: try:
client_id = request.client.host client_id = request.client.host
logger.debug(f"Запрос на выход из системы для клиента {client_id}") logger.debug(f"Запрос logout для client_id {client_id}")
return auth_service.logout(client_id) return auth_service.logout(client_id)
except HTTPException as e: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
logger.error(f"Не удалось выйти из системы: {type(e).__name__} - {str(e)}") logger.error(f"Ошибка в logout: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось выйти из системы")) raise HTTPException(status_code=500,
detail="Не удалось выйти из системы")
@router.get("/check_json") @router.get("/check_json")
async def check_users_file(translations: Translations = Depends(get_translations)): async def check_users_file():
"""Проверяет, существует ли файл users.json и содержит ли он пользователей. """Проверяет, существует ли файл users.json и содержит ли он пользователей"""
Возвращает информацию о состоянии файла и наличии пользователей.
Returns:
dict: Словарь с ключами:
- "exists" (bool): Существует ли файл.
- "has_users" (bool): Содержит ли файл пользователей.
- "message" (str): Сообщение с описанием результата.
Raises:
HTTPException: Если произошла ошибка при проверке файла (status_code=500).
"""
try: try:
file_path = "/etc/gonets/users.json" file_path = "/etc/gonets/users.json"
storage = JsonStorage(file_path=file_path) storage = JsonStorage(file_path=file_path)
@ -159,25 +97,34 @@ async def check_users_file(translations: Translations = Depends(get_translations
return { return {
"exists": False, "exists": False,
"has_users": False, "has_users": False,
"message": translations.gettext("Файл users.json не существует"), "message": "Файл users.json не существует",
} }
data = storage._read_data() data = storage._read_data()
has_users = len(data.get("users", [])) > 0 has_users = len(data.get("users", [])) > 0
if has_users: if has_users:
logger.info(f"Файл {file_path} существует и содержит пользователей") logger.info(
f"Файл {file_path} существует и содержит пользователей")
return { return {
"exists": True, "exists": True,
"has_users": True, "has_users": True,
"message": translations.gettext("Файл users.json существует и содержит пользователей"), "message":
"Файл users.json существует и содержит пользователей",
} }
else: else:
logger.info(f"Файл {file_path} существует, но не содержит пользователей") logger.info(
f"Файл {file_path} существует, но не содержит пользователей")
return { return {
"exists": True, "exists":
"has_users": False, True,
"message": translations.gettext("Файл users.json существует, но не содержит пользователей"), "has_users":
False,
"message":
"Файл users.json существует, но не содержит пользователей",
} }
except Exception as e: except Exception as e:
logger.error(f"Ошибка при проверке файла: {type(e).__name__} - {str(e)}") logger.error(
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка при проверке файла")) f"Ошибка при проверке файла users.json: {type(e).__name__} - {str(e)}"
)
raise HTTPException(status_code=500,
detail="Ошибка при проверке файла")

View file

@ -1,111 +1,56 @@
from typing import Dict, Any from typing import Dict, Any
from fastapi import APIRouter, Body, HTTPException, Depends from fastapi import APIRouter, Body, HTTPException
from services.ip_service import IPService from services.ip_service import IPService
from utils.i18n import get_translations
from babel.support import Translations
router = APIRouter() router = APIRouter()
ip_service = IPService() ip_service = IPService()
@router.post("/check_ip") @router.post("/check_ip")
async def check_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations)): async def check_ip_config(data: Dict[str, Any] = Body(...)):
"""Проверяет доступность IP-адреса перед его применением. """Проверяет доступность IP-адреса"""
Выполняет ARP-сканирование для проверки, занят ли указанный IP-адрес,
и возвращает результат проверки.
Args:
data (Dict[str, Any]): Словарь с данными конфигурации, включая
"ipv4_updates" с полем "ipv4.address-data" для нового IP-адреса.
translations: Объект переводов для текущего языка.
Returns:
Dict[str, Any]: Словарь с ключами:
- "available" (bool): Доступен ли IP-адрес.
- "message" (str): Сообщение о результате проверки.
Raises:
HTTPException: Если произошла внутренняя ошибка (status_code=500).
"""
try: try:
result = await ip_service.check_ip_config(data, translations) result = await ip_service.check_ip_config(data)
return result return result
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) detail=f"Ошибка проверки IP конфигурации: {str(e)}")
)
@router.post("/apply_ip") @router.post("/apply_ip")
async def apply_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations)): async def apply_ip_config(data: Dict[str, Any] = Body(...)):
"""Применяет проверенные IP-настройки к указанному сетевому профилю. """Применяет проверенные IP-настройки"""
Обновляет настройки соединения через NetworkManager, отключает и
переподключает устройство к сети.
Args:
data (Dict[str, Any]): Словарь с данными конфигурации, включая
"connection_id" и "ipv4_updates" с обновлениями IP-настроек.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключами:
- "success" (bool): Успешность операции.
- "message" (str): Сообщение об успешном применении настроек.
Raises:
HTTPException: Если подключение не найдено (status_code=404) или
произошла внутренняя ошибка (status_code=500).
"""
try: try:
await ip_service.apply_ip_config(data, translations) await ip_service.apply_ip_config(data)
return { return {
"success": True, "success":
"message": translations.gettext("Настройки сетевого профиля для {connection_id} применены").format( True,
connection_id=data["connection_id"] "message":
), f"Настройки сетевого профиля для {data['connection_id']} применены",
} }
except ValueError as e: except ValueError as e:
raise HTTPException(status_code=404, detail=translations.gettext(str(e))) raise HTTPException(status_code=404, detail=str(e))
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) detail=f"Ошибка проверки IP конфигурации: {str(e)}")
)
@router.post("/save_ip") @router.post("/save_ip")
async def save_ip_config(data: Dict[str, Any] = Body(...), translations: Translations = Depends(get_translations)): async def save_ip_config(data: Dict[str, Any] = Body(...)):
"""Применяет проверенные IP-настройки для отката сетевого профиля. """Применяет проверенные IP-настройки (Нужен для того, чтобы откатить настройки сетевого профиля обратно)"""
Используется для сохранения и отката настроек сетевого профиля к предыдущему
состоянию через NetworkManager.
Args:
data (Dict[str, Any]): Словарь с данными конфигурации, включая
"connection_id" и "ipv4_updates" с обновлениями IP-настроек.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключами:
- "success" (bool): Успешность операции.
- "message" (str): Сообщение об успешном применении настроек.
Raises:
HTTPException: Если подключение не найдено (status_code=404) или
произошла внутренняя ошибка (status_code=500).
"""
try: try:
await ip_service.apply_ip_config(data, translations) await ip_service.apply_ip_config(data)
return { return {
"success": True, "success":
"message": translations.gettext("Настройки сетевого профиля для {connection_id} применены").format( True,
connection_id=data["connection_id"] "message":
), f"Настройки сетевого профиля для {data['connection_id']} применены",
} }
except ValueError as e: except ValueError as e:
raise HTTPException(status_code=404, detail=translations.gettext(str(e))) raise HTTPException(status_code=404, detail=str(e))
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=translations.gettext("Ошибка проверки IP конфигурации: {error}").format(error=str(e)) detail=f"Ошибка проверки IP конфигурации: {str(e)}")
)

View file

@ -1,6 +1,7 @@
from fastapi import APIRouter, Body, HTTPException, Depends from fastapi import APIRouter, Body, HTTPException
import logging import logging
from typing import Dict, Tuple, List, Union from typing import Dict, Tuple, List, Union
from services.network_service import NetworkService from services.network_service import NetworkService
from models.network import ( from models.network import (
NetworkInterfacesContainer, NetworkInterfacesContainer,
@ -9,8 +10,6 @@ from models.network import (
NetworkInterface, NetworkInterface,
WiFiDevice, WiFiDevice,
) )
from utils.i18n import get_translations, _, _load_translations
from babel.support import Translations
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -18,19 +17,13 @@ logger = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()
network_service = NetworkService() network_service = NetworkService()
class ContainerPositionManager:
"""Класс для управления позициями и состоянием контейнеров.
Хранит и обновляет координаты (x, y) и состояние (isOpen) для контейнеров class ContainerPositionManager:
активации и деактивации. """
Класс для управления позициями и состоянием контейнеров
""" """
def __init__(self): def __init__(self):
"""Инициализирует менеджер позиций контейнеров.
Создаёт начальные позиции и состояния для контейнеров "activation" и
"deactivation" с координатами (0, 0) и закрытым состоянием (False).
"""
self.positions: Dict[str, Dict[str, Union[int, bool]]] = { self.positions: Dict[str, Dict[str, Union[int, bool]]] = {
"activation": { "activation": {
"x": 0, "x": 0,
@ -44,66 +37,37 @@ class ContainerPositionManager:
}, },
} }
def get_position_and_state(self, container_name: str, translations: Translations) -> Tuple[int, int, bool]: def get_position_and_state(self,
"""Получает текущие координаты и состояние указанного контейнера. container_name: str) -> Tuple[int, int, bool]:
Args:
container_name (str): Имя контейнера ("activation" или "deactivation").
translations: Объект переводов для текущего языка.
Returns:
Tuple[int, int, bool]: Кортеж с координатами (x, y) и состоянием (isOpen).
Raises:
ValueError: Если указан неверный идентификатор контейнера.
"""
container = self.positions.get(container_name) container = self.positions.get(container_name)
if not container: if not container:
raise ValueError(translations.gettext("Неверное имя контейнера: {container_name}").format(container_name=container_name)) raise ValueError(f"Неверное имя контейнера: {container_name}")
return container["x"], container["y"], container["isOpen"] return container["x"], container["y"], container["isOpen"]
def update_position(self, container_id: int, x: int, y: int, isOpen: bool) -> None: def update_position(self, container_id: int, x: int, y: int,
"""Обновляет позицию и состояние контейнера по его идентификатору. isOpen: bool) -> None:
Args:
container_id (int): Идентификатор контейнера (1 для "activation", 2 для "deactivation").
x (int): Новая координата X.
y (int): Новая координата Y.
isOpen (bool): Новое состояние контейнера (открыт/закрыт).
Raises:
ValueError: Если container_id не соответствует ни одному контейнеру.
"""
container_name = "activation" if container_id == 1 else "deactivation" container_name = "activation" if container_id == 1 else "deactivation"
self.positions[container_name] = {"x": x, "y": y, "isOpen": isOpen} self.positions[container_name] = {"x": x, "y": y, "isOpen": isOpen}
container_manager = ContainerPositionManager() container_manager = ContainerPositionManager()
async def get_network_data(translations: Translations = Depends(get_translations)) -> Dict[str, Dict]:
"""Возвращает текущие сетевые данные в виде словаря.
Получает данные о сетевых интерфейсах, разделяет их на активные и неактивные, async def get_network_data() -> Dict[str, Dict]:
и возвращает их в формате Activation и Deactivation с учётом позиций и состояний
контейнеров.
Args:
translations: Объект переводов для текущего языка.
Returns:
Dict[str, Dict]: Словарь с ключами "activation" и "deactivation", содержащими
данные о соответствующих контейнерах.
Raises:
HTTPException: Если произошла ошибка при получении данных (status_code=500).
""" """
logger.debug("Calling get_network_data()") Возвращает текущие сетевые данные в виде словаря
"""
logger.debug("Вызов get_network_data()")
try: try:
network_data = await network_service.get_network_interfaces_container() network_data = await network_service.get_network_interfaces_container()
activation_interfaces, deactivation_interfaces = _split_interfaces(network_data.interfaces) activation_interfaces, deactivation_interfaces = _split_interfaces(
network_data.interfaces)
activation_x, activation_y, activation_is_open = container_manager.get_position_and_state("activation", translations) activation_x, activation_y, activation_is_open = (
deactivation_x, deactivation_y, deactivation_is_open = container_manager.get_position_and_state("deactivation", translations) container_manager.get_position_and_state("activation"))
deactivation_x, deactivation_y, deactivation_is_open = (
container_manager.get_position_and_state("deactivation"))
activation = Activation( activation = Activation(
id=1, id=1,
@ -125,20 +89,17 @@ async def get_network_data(translations: Translations = Depends(get_translations
"deactivation": deactivation.model_dump(), "deactivation": deactivation.model_dump(),
} }
except Exception as e: except Exception as e:
logger.error(f"Error in get_network_data: {e}") logger.error(f"Ошибка в get_network_data: {e}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить сетевые данные")) raise HTTPException(status_code=500,
detail="Не удалось получить сетевые данные")
def _split_interfaces( def _split_interfaces(
interfaces: List[Union[NetworkInterface, WiFiDevice]], interfaces: List[Union[NetworkInterface, WiFiDevice]],
) -> Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]: ) -> Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[
"""Разделяет интерфейсы на активные и неактивные. NetworkInterface, WiFiDevice]]]:
"""
Args: Разделяет интерфейсы на активные и неактивные
interfaces (List[Union[NetworkInterface, WiFiDevice]]): Список сетевых интерфейсов.
Returns:
Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]:
Кортеж из списков активных и неактивных интерфейсов.
""" """
activation_interfaces = [] activation_interfaces = []
deactivation_interfaces = [] deactivation_interfaces = []
@ -149,37 +110,21 @@ def _split_interfaces(
deactivation_interfaces.append(interface) deactivation_interfaces.append(interface)
return activation_interfaces, deactivation_interfaces return activation_interfaces, deactivation_interfaces
@router.post("/update_container_position") @router.post("/update_container_position")
async def update_container_position(data: Dict[str, Union[int, bool]] = Body(...), translations: Translations = Depends(get_translations)): async def update_container_position(data: Dict[str, Union[int,
"""Обновляет позицию и состояние контейнера. bool]] = Body(...)):
"""
Принимает данные о новом положении и состоянии контейнера и обновляет их Обновляет позицию и состояние контейнера
в менеджере позиций.
Args:
data (Dict[str, Union[int, bool]]): Словарь с ключами:
- "container_id" (int): Идентификатор контейнера (1 или 2).
- "container_x" (int): Новая координата X.
- "container_y" (int): Новая координата Y.
- "isOpen" (bool): Новое состояние контейнера (открыт/закрыт).
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключами:
- "success" (bool): Успешность операции.
- "message" (str): Сообщение об успешном обновлении.
Raises:
HTTPException: Если отсутствуют обязательные поля (status_code=400) или
типы данных неверны.
""" """
try: try:
required_keys = [ required_keys = [
"container_id", "container_x", "container_y", "isOpen" "container_id", "container_x", "container_y", "isOpen"
] ]
if not all(key in data for key in required_keys): if not all(key in data for key in required_keys):
raise ValueError( raise ValueError(
translations.gettext("Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen") "Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen"
) )
container_id = data["container_id"] container_id = data["container_id"]
@ -187,39 +132,33 @@ async def update_container_position(data: Dict[str, Union[int, bool]] = Body(...
container_y = data["container_y"] container_y = data["container_y"]
is_open = data["isOpen"] is_open = data["isOpen"]
# Проверка типов
if (not isinstance(container_id, int) if (not isinstance(container_id, int)
or not isinstance(container_x, int) or not isinstance(container_x, int)
or not isinstance(container_y, int) or not isinstance(container_y, int)
or not isinstance(is_open, bool)): or not isinstance(is_open, bool)):
raise ValueError(translations.gettext("Неверный тип данных в запросе")) raise ValueError("Неверный тип данных в запросе")
container_manager.update_position(container_id, container_x, container_y, is_open) container_manager.update_position(container_id, container_x,
container_y, is_open)
return { return {
"success": True, "success": True,
"message": translations.gettext("Позиция и состояние контейнера обновлены") "message": "Позиция и состояние контейнера обновлены"
} }
except Exception as e: except Exception as e:
logger.error(f"Error updating container position: {e}") logger.error(f"Ошибка обновления позиции контейнера: {e}")
raise HTTPException(status_code=400, detail=translations.gettext("Не удалось обновить положение контейнера: {error}").format(error=str(e))) raise HTTPException(
status_code=400,
detail=f"Не удалось обновить положение контейнера: {str(e)}",
)
@router.get("/get_hostname") @router.get("/get_hostname")
async def get_hostname(translations: Translations = Depends(get_translations)): async def get_hostname():
"""Возвращает имя хоста системы.
Выполняет запрос к сервису для получения имени хоста.
Args:
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с ключом "hostname" и значением имени хоста.
Raises:
HTTPException: Если произошла ошибка при получении имени хоста (status_code=500).
"""
try: try:
hostname_info = await network_service.get_hostname() hostname_info = await network_service.get_hostname()
return hostname_info return hostname_info
except Exception as e: except Exception as e:
logger.error(f"Error in get_hostname: {e}") logger.error(f"Ошибка в get_hostname: {e}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось получить имя хоста")) raise HTTPException(status_code=500,
detail="Не удалось получить имя хоста")

View file

@ -1,209 +0,0 @@
import os
import logging
import subprocess
from typing import Optional
from fastapi import APIRouter, HTTPException, Depends, Response, Request, Body
from starlette.responses import FileResponse
from services.settings_service import SettingsService
from utils.i18n import get_translations
from babel.support import Translations
from models.subnet import SubnetScanRequest # Предполагается, что модель определена
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
router = APIRouter()
settings_service = SettingsService()
@router.post("/clear_subnet")
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:
dict: Словарь с сообщением об успешной очистке.
Raises:
HTTPException: Если произошла ошибка очистки (status_code=500).
"""
try:
success = settings_service.clear_subnet(subnet)
if success:
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:
logger.error(f"Error clearing subnet data: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
@router.post("/clear_wifi")
def clear_wifi(translations: Translations = Depends(get_translations)):
"""Очищает данные точек доступа Wi-Fi, сохранённые в wifi.json.
Удаляет все записи о точках доступа, оставляя файл пустым.
Args:
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешной очистке.
Raises:
HTTPException: Если произошла ошибка очистки (status_code=500).
"""
try:
success = settings_service.clear_wifi()
if success:
return {"message": translations.gettext("Данные точек доступа Wi-Fi успешно очищены")}
else:
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить данные точек доступа Wi-Fi"))
except Exception as e:
logger.error(f"Error clearing wifi data: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
@router.post("/clear_all_data")
def clear_all_data(translations: Translations = Depends(get_translations)):
"""Очищает данные узлов подсети и точек доступа Wi-Fi в обоих файлах.
Удаляет все записи из devices.json и wifi.json.
Args:
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешной очистке.
Raises:
HTTPException: Если произошла ошибка очистки (status_code=500).
"""
try:
success = settings_service.clear_all_data()
if success:
return {"message": translations.gettext("Все сетевые данные успешно очищены")}
else:
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось очистить все сетевые данные"))
except Exception as e:
logger.error(f"Error clearing all data: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка очистки: {error}").format(error=str(e)))
WIFI_PATH = "storage/wifi/wifi.json"
@router.post("/export_devices", response_class=FileResponse)
async def export_devices(request: SubnetScanRequest = Body(...), translations: Translations = Depends(get_translations)):
"""
Экспортирует файл с данными устройств в формате JSON для указанной подсети.
Args:
request (SubnetScanRequest): Объект с полем subnet в формате CIDR.
translations: Объект переводов для текущего языка.
Returns:
FileResponse: Файл <subnet>.json для скачивания.
Raises:
HTTPException: Если файл не найден (status_code=404).
"""
subnet = request.subnet
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("Файл для подсети {subnet} не найден").format(subnet=subnet))
logger.info(f"Exporting file: {file_path}")
return FileResponse(
path=file_path,
filename=f"{subnet.replace('/', '_')}.json",
media_type="application/json"
)
@router.get("/export_wifi", response_class=FileResponse)
async def export_wifi(translations: Translations = Depends(get_translations)):
"""
Экспортирует файл с данными устройств в формате JSON.
Args:
translations: Объект переводов для текущего языка.
Returns:
FileResponse: Файл wifi.json для скачивания.
Raises:
HTTPException: Если файл не найден (status_code=404).
"""
if not os.path.exists(WIFI_PATH):
logger.warning(f"Export file not found: {WIFI_PATH}")
raise HTTPException(status_code=404, detail=translations.gettext("Файл не найден"))
logger.info(f"Exporting file: {WIFI_PATH}")
return FileResponse(
path=WIFI_PATH,
filename="wifi.json",
media_type="application/json"
)
@router.post("/set_language")
def set_language(locale: str, response: Response, translations: Translations = Depends(get_translations)):
"""
Устанавливает куку locale для смены языка пользователя.
Args:
locale (str): Используемый язык.
response (Response): Запрос.
translations: Объект переводов текущего языка.
Returns:
dict: Сообщение об успешной установке языка.
"""
lc_messages_path = os.path.join(os.path.dirname(__file__), '..', '..', 'locales', locale, 'LC_MESSAGES')
if not os.path.isdir(lc_messages_path):
raise HTTPException(
status_code=400,
detail=translations.gettext("Язык '{locale}' не поддерживается.").format(locale=locale)
)
has_mo_or_po = any(f.endswith('.mo') or f.endswith('.po') for f in os.listdir(lc_messages_path))
if not has_mo_or_po:
raise HTTPException(
status_code=400,
detail=translations.gettext("Язык '{locale}' не поддерживается (нет файлов переводов).").format(locale=locale)
)
response.set_cookie(key="locale", value=locale, path="/", httponly=True)
return {"message": translations.gettext("Язык успешно установлен: {locale}").format(locale=locale)}
@router.get("/get_system_language")
async def get_system_language(request: Request, translations: Translations = Depends(get_translations)):
"""Выводит текущий язык, установленный в системе на основе locale.
Выполняет команду 'locale' в системе и извлекает параметры LANG
Args:
request (Request): Объект запроса для доступа к параметрам.
translations: Объект переводов текущего языка.
Returns:
dict: Словарь с текущим языком системы.
Raises:
HTTPException: Если не удалось извлечь системный язык (status_code=500).
"""
try:
result = subprocess.run(['locale'], capture_output=True, text=True, check=True)
output = result.stdout
lang = next((line.split('=')[1].strip() for line in output.splitlines() if line.startswith('LANG=')), None)
if lang:
return {"language": lang}
else:
logger.warning(f"No locale found: {output}")
return {"language": 'unknown'}
except subprocess.CalledProcessError as e:
logger.error(f"Error executing locale command: {e}")
raise HTTPException(status_code=500, detail=translations.gettext("Не удалось определить язык в системе"))
except Exception as e:
logger.error(f"Unexpected error getting system language: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка при определении языка в системе {error}").format(error=str(e)))

View file

@ -1,352 +1,159 @@
from fastapi import APIRouter, HTTPException, Body, Depends from fastapi import APIRouter, HTTPException
from models.subnet import SubnetScanResult, SubnetScanRequest from typing import List, Dict
from models.subnet import SubnetScanResult
from models.wifi import WifiScanResult from models.wifi import WifiScanResult
from models.request import NoteRequest, WifiConnectRequest, WifiDisconnectRequest from models.request import NoteRequest, WifiConnectRequest, WifiDisconnectRequest
from services.subnet_service import SubnetService, get_subnet_service from services.subnet_service import SubnetService
from services.wifi_service import WifiService from services.wifi_service import WifiService
from services.connect_service import WifiConnectionService from services.connect_service import WifiConnectionService
from utils.i18n import get_translations, _
from babel.support import Translations
import logging
router = APIRouter() router = APIRouter()
logging.basicConfig(level=logging.INFO) @router.get("/devices", response_model=SubnetScanResult)
logger = logging.getLogger(__name__) def scan_subnet(subnet: str = "192.168.1.0/24", ports: List[int] = None):
"""Сканирует локальную подсеть и возвращает данные об устройствах"""
@router.post("/devices", response_model=SubnetScanResult)
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:
SubnetScanResult: Объект с результатами сканирования (устройства и их данные).
Raises:
HTTPException: Если произошла ошибка сканирования (status_code=500).
"""
try: try:
# Если subnet пустая строка, определяем автоматически subnet_service = SubnetService()
subnet = request.subnet.strip() if request.subnet else None result = subnet_service.scan_subnet(subnet, ports)
logger.info(f"Requested subnet scan for: {subnet or 'auto-detected'}")
result = subnet_service.scan_subnet(subnet)
if not result.devices and result.error:
logger.warning(f"No devices found in subnet {subnet or 'auto-detected'}")
return result return result
except Exception as e: except Exception as e:
logger.error(f"Error in scan_subnet: {str(e)}") raise HTTPException(status_code=500, detail=f"Ошибка сканирования: {str(e)}")
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования подсети: {error}").format(error=str(e)))
@router.get("/wifi", response_model=WifiScanResult) @router.get("/wifi", response_model=WifiScanResult)
def scan_wifi(timeout: int = 30, translations: Translations = Depends(get_translations)): def scan_wifi(timeout: int = 30):
"""Сканирует Wi-Fi точки доступа и возвращает данные о них. """Сканирует Wi-Fi точки доступа и возвращает данные о них"""
Выполняет сканирование доступных Wi-Fi сетей с указанным таймаутом.
Args:
timeout (int, optional): Таймаут сканирования в секундах. По умолчанию 30.
translations: Объект переводов для текущего языка.
Returns:
WifiScanResult: Объект с результатами сканирования (список Wi-Fi сетей).
Raises:
HTTPException: Если произошла ошибка сканирования Wi-Fi (status_code=500).
"""
try: try:
wifi_service = WifiService() wifi_service = WifiService()
result = wifi_service.scan_wifi(timeout=timeout) result = wifi_service.scan_wifi(timeout=timeout)
return result return result
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка сканирования Wi-Fi: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка сканирования Wi-Fi: {str(e)}")
@router.post("/devices/{mac}/note") @router.post("/devices/{mac}/note")
def add_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations)): def add_note(mac: str, request: NoteRequest):
"""Добавляет заметку для устройства по MAC-адресу. """Добавляет заметку для устройства по MAC-адресу"""
Привязывает заметку к устройству, идентифицируемому по MAC-адресу.
Args:
mac (str): MAC-адрес устройства.
request (NoteRequest): Объект с полем "note" для текста заметки.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном добавлении заметки.
Raises:
HTTPException: Если устройство не найдено (status_code=404), заметка уже
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
success, result = subnet_service.add_note(mac, request.note, translations) subnet_service = SubnetService()
success, error_message = subnet_service.add_note(mac, request.note)
if not success: if not success:
if result[0] == "not_found": if "не найдено" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_exists": elif "уже существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} уже существует").format(mac=mac)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка '{request.note}' добавлена для устройства {mac}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка добавления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка добавления заметки: {str(e)}")
@router.put("/devices/{mac}/note") @router.put("/devices/{mac}/note")
def update_note(mac: str, request: NoteRequest, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations)): def update_note(mac: str, request: NoteRequest):
"""Обновляет заметку для устройства по MAC-адресу. """Обновляет заметку для устройства по MAC-адресу"""
Изменяет существующую заметку, привязанную к устройству.
Args:
mac (str): MAC-адрес устройства.
request (NoteRequest): Объект с полем "note" для нового текста заметки.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном обновлении заметки.
Raises:
HTTPException: Если устройство не найдено (status_code=404), заметка не
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
success, result = subnet_service.update_note(mac, request.note, translations) subnet_service = SubnetService()
success, error_message = subnet_service.update_note(mac, request.note)
if not success: if not success:
if result[0] == "not_found": if "не найдено" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_not_exists": elif "не существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка '{request.note}' обновлена для устройства {mac}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка обновления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка обновления заметки: {str(e)}")
@router.delete("/devices/{mac}/note") @router.delete("/devices/{mac}/note")
def delete_note(mac: str, subnet_service: SubnetService = Depends(get_subnet_service), translations: Translations = Depends(get_translations)): def delete_note(mac: str):
"""Удаляет заметку для устройства по MAC-адресу. """Удаляет заметку для устройства по MAC-адресу"""
Удаляет привязанную заметку для указанного устройства.
Args:
mac (str): MAC-адрес устройства.
subnet_service: Сервис сканирования подсети.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном удалении заметки.
Raises:
HTTPException: Если устройство не найдено (status_code=404), заметка не
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
success, result = subnet_service.delete_note(mac, translations) subnet_service = SubnetService()
success, error_message = subnet_service.delete_note(mac)
if not success: if not success:
if result[0] == "not_found": if "не найдено" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Устройство с MAC {mac} не найдено").format(mac=mac)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_not_exists": elif "не существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для устройства с MAC {mac} не существует").format(mac=mac)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка удалена для устройства {mac}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка удаления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка удаления заметки: {str(e)}")
@router.post("/wifi/{ssid}/note") @router.post("/wifi/{ssid}/note")
def add_wifi_note(ssid: str, request: NoteRequest, translations: Translations = Depends(get_translations)): def add_wifi_note(ssid: str, request: NoteRequest):
"""Добавляет заметку для сети Wi-Fi по SSID. """Добавляет заметку для сети Wi-Fi по SSID"""
Привязывает заметку к Wi-Fi сети, идентифицируемой по SSID.
Args:
ssid (str): Идентификатор Wi-Fi сети.
request (NoteRequest): Объект с полем "note" для текста заметки.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном добавлении заметки.
Raises:
HTTPException: Если сеть не найдена (status_code=404), заметка уже
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
wifi_service = WifiService() wifi_service = WifiService()
success, result = wifi_service.add_note(ssid, request.note) success, error_message = wifi_service.add_note(ssid, request.note)
if not success: if not success:
if result[0] == "not_found": if "не найдена" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_exists": elif "уже существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} уже существует").format(ssid=ssid)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка '{request.note}' добавлена для сети с SSID {ssid}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка добавления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка добавления заметки: {str(e)}")
@router.put("/wifi/{ssid}/note") @router.put("/wifi/{ssid}/note")
def update_wifi_note(ssid: str, request: NoteRequest, translations: Translations = Depends(get_translations)): def update_wifi_note(ssid: str, request: NoteRequest):
"""Обновляет заметку для сети Wi-Fi по SSID. """Обновляет заметку для сети Wi-Fi по SSID"""
Изменяет существующую заметку, привязанную к Wi-Fi сети.
Args:
ssid (str): Идентификатор Wi-Fi сети.
request (NoteRequest): Объект с полем "note" для нового текста заметки.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном обновлении заметки.
Raises:
HTTPException: Если сеть не найдена (status_code=404), заметка не
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
wifi_service = WifiService() wifi_service = WifiService()
success, result = wifi_service.update_note(ssid, request.note) success, error_message = wifi_service.update_note(ssid, request.note)
if not success: if not success:
if result[0] == "not_found": if "не найдена" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_not_exists": elif "не существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка '{request.note}' обновлена для сети с SSID {ssid}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка обновления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка обновления заметки: {str(e)}")
@router.delete("/wifi/{ssid}/note") @router.delete("/wifi/{ssid}/note")
def delete_wifi_note(ssid: str, translations: Translations = Depends(get_translations)): def delete_wifi_note(ssid: str):
"""Удаляет заметку для сети Wi-Fi по SSID. """Удаляет заметку для сети Wi-Fi по SSID"""
Удаляет привязанную заметку для указанной Wi-Fi сети.
Args:
ssid (str): Идентификатор Wi-Fi сети.
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном удалении заметки.
Raises:
HTTPException: Если сеть не найдена (status_code=404), заметка не
существует (status_code=400), или произошла внутренняя ошибка (status_code=500).
"""
try: try:
wifi_service = WifiService() wifi_service = WifiService()
success, result = wifi_service.delete_note(ssid) success, error_message = wifi_service.delete_note(ssid)
if not success: if not success:
if result[0] == "not_found": if "не найдена" in error_message:
raise HTTPException(status_code=404, detail=translations.gettext("Сеть с SSID {ssid} не найдена").format(ssid=ssid)) raise HTTPException(status_code=404, detail=error_message)
elif result[0] == "note_not_exists": elif "не существует" in error_message:
raise HTTPException(status_code=400, detail=translations.gettext("Заметка для сети с SSID {ssid} не существует").format(ssid=ssid)) raise HTTPException(status_code=400, detail=error_message)
else: return {"message": f"Заметка удалена для сети с SSID {ssid}"}
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: except HTTPException as e:
raise e raise e
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка удаления заметки: {error}").format(error=str(e))) raise HTTPException(status_code=500, detail=f"Ошибка удаления заметки: {str(e)}")
@router.post("/wifi/connect") @router.post("/wifi/connect")
def connect_to_wifi(request: WifiConnectRequest, translations: Translations = Depends(get_translations)): def connect_to_wifi(request: WifiConnectRequest):
"""Подключается к Wi-Fi сети по SSID и паролю. """Подключается к Wi-Fi сети по SSID и паролю."""
wifi_service = WifiConnectionService()
Выполняет подключение к указанной Wi-Fi сети с использованием предоставленных success, message = wifi_service.connect(request.ssid, request.password)
учетных данных. if not success:
raise HTTPException(status_code=400, detail=message)
Args: return {"message": f"Успешно подключено к {request.ssid}"}
request (WifiConnectRequest): Объект с полями "ssid" и "password".
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном подключении.
Raises:
HTTPException: Если подключение не удалось (status_code=400).
"""
try:
wifi_service = WifiConnectionService(translations)
success, message = wifi_service.connect(request.ssid, request.password)
if not success:
raise HTTPException(status_code=400, detail=translations.gettext(message))
return {"message": translations.gettext("Успешно подключено к {ssid}").format(ssid=request.ssid)}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка подключения к Wi-Fi: {error}").format(error=str(e)))
@router.post("/wifi/disconnect") @router.post("/wifi/disconnect")
def disconnect_from_wifi(request: WifiDisconnectRequest, translations: Translations = Depends(get_translations)): def disconnect_from_wifi(request: WifiDisconnectRequest):
"""Отключается от Wi-Fi сети по указанному SSID. """Отключается от Wi-Fi сети по указанному SSID."""
wifi_service = WifiConnectionService()
Выполняет отключение от указанной Wi-Fi сети. success, message = wifi_service.disconnect(request.ssid)
if not success:
Args: raise HTTPException(status_code=400, detail=message)
request (WifiDisconnectRequest): Объект с полем "ssid". return {"message": message or f"Успешно отключено от {request.ssid}"}
translations: Объект переводов для текущего языка.
Returns:
dict: Словарь с сообщением об успешном отключении.
Raises:
HTTPException: Если отключение не удалось (status_code=400).
"""
try:
wifi_service = WifiConnectionService(translations)
success, message = wifi_service.disconnect(request.ssid)
if not success:
raise HTTPException(status_code=400, detail=translations.gettext(message))
return {"message": translations.gettext(message or "Успешно отключено от {ssid}").format(ssid=request.ssid)}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка отключения от Wi-Fi: {error}").format(error=str(e)))
@router.get("/wifi/status") @router.get("/wifi/status")
def get_wifi_status(translations: Translations = Depends(get_translations)): def get_wifi_status():
"""Проверяет статус текущего подключения к Wi-Fi. """Проверяет статус текущего подключения к Wi-Fi."""
wifi_service = WifiConnectionService()
Возвращает информацию о текущем состоянии подключения и SSID, если подключение активно. success, message = wifi_service.get_status()
if not success:
Args: raise HTTPException(status_code=400, detail=message)
translations: Объект переводов для текущего языка. return {"status": "connected", "ssid": message}
Returns:
dict: Словарь с ключами:
- "status" (str): Состояние ("connected" или другое).
- "ssid" (str, optional): SSID текущей сети, если подключено.
Raises:
HTTPException: Если проверка статуса не удалась (status_code=400).
"""
try:
wifi_service = WifiConnectionService(translations)
success, message = wifi_service.get_status()
if not success:
raise HTTPException(status_code=400, detail=translations.gettext(message))
return {"status": translations.gettext("connected"), "ssid": message}
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=translations.gettext("Ошибка проверки статуса Wi-Fi: {error}").format(error=str(e)))

View file

@ -9,29 +9,14 @@ router = APIRouter()
class RuleNumbers(BaseModel): class RuleNumbers(BaseModel):
"""Модель запроса на удаление UFW правил.
Атрибуты:
- ``numbers`` (List[int]): Список номеров правил для удаления.
"""
numbers: List[int] = Field( numbers: List[int] = Field(
..., description="Список номеров правил для удаления") ..., description="Список номеров правил для удаления")
@router.get("/rules", response_model=UFWRulesResponse) @router.get("/rules", response_model=UFWRulesResponse)
async def get_ufw_rules(): async def get_ufw_rules():
"""Возвращает все правила UFW. """
Получает все правила UFW с помощью сервиса UFWService и возвращает их в формате JSON.
Осуществляет обращение к сервису `UFWService` для чтения текущего списка
правил брандмауэра и возвращает результат в сериализованном виде для клиента.
Returns:
UFWRulesResponse: Структура с коллекцией правил и сопутствующей
информацией о состоянии.
Raises:
HTTPException: В случае ошибок взаимодействия с UFW (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -42,13 +27,8 @@ async def get_ufw_rules():
@router.get("/status", response_model=UFWStatus) @router.get("/status", response_model=UFWStatus)
async def get_ufw_status(): async def get_ufw_status():
"""Возвращает текущий статус UFW. """
Получает текущий статус UFW.
Returns:
UFWStatus: Объект со сведениями о включенности UFW и деталями статуса.
Raises:
HTTPException: Если не удалось получить статус (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -59,13 +39,8 @@ async def get_ufw_status():
@router.post("/enable") @router.post("/enable")
async def enable_ufw(): async def enable_ufw():
"""Включает UFW. """
Включает UFW.
Returns:
dict: Словарь с ключами `status` и `message` при успешном выполнении.
Raises:
HTTPException: Если включение завершилось неуспешно (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -81,13 +56,8 @@ async def enable_ufw():
@router.post("/disable") @router.post("/disable")
async def disable_ufw(): async def disable_ufw():
"""Выключает UFW. """
Выключает UFW.
Returns:
dict: Словарь с ключами `status` и `message` при успешном выполнении.
Raises:
HTTPException: Если выключение завершилось неуспешно (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -103,19 +73,8 @@ async def disable_ufw():
@router.post("/rules/in") @router.post("/rules/in")
async def add_ufw_in_rules(rules: List[UFWRule]): async def add_ufw_in_rules(rules: List[UFWRule]):
"""Добавляет входящие (in) правила UFW. """
Добавляет входящие (in) правила UFW.
Args:
rules (List[UFWRule]): Список правил, определяющих направления,
порты/диапазоны/протоколы и действия (allow/deny/reject).
Returns:
dict: Итог выполнения с количеством успешно добавленных правил и деталями
по каждому правилу.
Raises:
HTTPException: Если UFW отключен (status_code=400) или при внутренних
ошибках (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -149,17 +108,8 @@ async def add_ufw_in_rules(rules: List[UFWRule]):
@router.post("/rules/out") @router.post("/rules/out")
async def add_ufw_out_rules(rules: List[UFWRule]): async def add_ufw_out_rules(rules: List[UFWRule]):
"""Добавляет исходящие (out) правила UFW. """
Добавляет исходящие (out) правила UFW.
Args:
rules (List[UFWRule]): Список правил исходящего трафика для добавления.
Returns:
dict: Объект статуса с сообщением об успешном добавлении.
Raises:
HTTPException: Если UFW отключен (status_code=400) или при внутренних
ошибках (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:
@ -187,18 +137,8 @@ async def add_ufw_out_rules(rules: List[UFWRule]):
@router.delete("/rules") @router.delete("/rules")
async def delete_ufw_rules(rule_numbers: RuleNumbers): async def delete_ufw_rules(rule_numbers: RuleNumbers):
"""Удаляет правила UFW по их номерам. """
Удаляет правила UFW по их номерам.
Args:
rule_numbers (RuleNumbers): Номера правил, подлежащих удалению (как в
выводе `ufw status numbered`).
Returns:
dict: Информация об общем количестве удаляемых правил, числе успешных и
возможной ошибке.
Raises:
HTTPException: При ошибках удаления (status_code=500).
""" """
ufw_service = UFWService() ufw_service = UFWService()
try: try:

View file

@ -2,10 +2,8 @@ import asyncio
import logging import logging
from typing import Set from typing import Set
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from starlette.websockets import WebSocketState from starlette.websockets import WebSocketState
from utils.i18n import get_translations_ws, _, _load_translations
from babel.support import Translations
from .network import update_container_position, get_network_data from .network import update_container_position, get_network_data
@ -14,57 +12,27 @@ router = APIRouter()
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class WebSocketManager:
"""Класс для управления подключениями к WebSocket-серверу.
Обрабатывает подключение, отключение и трансляцию сообщений для клиентов, class WebSocketManager:
подключённых через WebSocket. """
Класс для управления подключениями к WebSocket-серверу
""" """
def __init__(self): def __init__(self):
"""Инициализирует менеджер подключений к WebSocket-серверу. """Инициализация менеджера подключений к WebSocket-серверу"""
Создаёт пустое множество для хранения активных соединений.
"""
self.active_connections: Set[WebSocket] = set() self.active_connections: Set[WebSocket] = set()
async def connect(self, websocket: WebSocket): async def connect(self, websocket: WebSocket):
"""Подключает клиента к WebSocket-серверу. """Подключение клиента к WebSocket-серверу"""
Принимает подключение и добавляет WebSocket в список активных соединений.
Args:
websocket (WebSocket): Объект WebSocket для подключения.
Raises:
Exception: Если произошла ошибка при принятии подключения.
"""
await websocket.accept() await websocket.accept()
self.active_connections.add(websocket) self.active_connections.add(websocket)
def disconnect(self, websocket: WebSocket): def disconnect(self, websocket: WebSocket):
"""Отключает клиента от WebSocket-серверу. """Отключение клиента от WebSocket-серверу"""
Удаляет WebSocket из списка активных соединений.
Args:
websocket (WebSocket): Объект WebSocket для отключения.
"""
self.active_connections.remove(websocket) self.active_connections.remove(websocket)
async def broadcast(self, message: dict): async def broadcast(self, message: dict):
"""Транслирует сообщения всем подключённым клиентам. """Трансляция сообщений всем подключенным клиентам"""
Отправляет JSON-данные всем активным соединениям, обрабатывая
отключения клиентов.
Args:
message (dict): Сообщение для трансляции в формате JSON.
Raises:
WebSocketDisconnect: Если клиент неожиданно отключился.
Exception: Если произошла другая ошибка при отправке.
"""
for connection in list(self.active_connections): for connection in list(self.active_connections):
try: try:
if connection.client_state == WebSocketState.CONNECTED: if connection.client_state == WebSocketState.CONNECTED:
@ -72,83 +40,60 @@ class WebSocketManager:
except WebSocketDisconnect: except WebSocketDisconnect:
self.disconnect(connection) self.disconnect(connection)
except Exception as e: except Exception as e:
logger.error(f"Broadcast error: {e}") logger.error(f"Сообщение об ошибке трансляции: {e}")
self.disconnect(connection) self.disconnect(connection)
manager = WebSocketManager() manager = WebSocketManager()
@router.websocket("/api/v1") @router.websocket("/api/v1")
async def websocket_endpoint(websocket: WebSocket, translations: Translations = Depends(get_translations_ws)): async def websocket_endpoint(websocket: WebSocket):
"""Обработчик WebSocket-соединения. """Обработчик WebSocket-соединения"""
Управляет соединением клиента, принимает данные для обновления позиций
контейнеров и транслирует сетевые данные.
Args:
websocket (WebSocket): Объект WebSocket для обработки соединения.
translations: Объект переводов для текущего языка.
Raises:
WebSocketDisconnect: Если клиент отключился.
Exception: Если произошла ошибка при обработке данных.
"""
await manager.connect(websocket) await manager.connect(websocket)
try: try:
while True: while True:
data = await websocket.receive_json() data = await websocket.receive_json()
logger.info(f"Received data: {data}") logger.info(f"Полученные данные: {data}")
if all(key in data for key in ["container_id", "container_x", "container_y"]): if all(key in data
await update_container_position(data, translations=translations) for key in ["container_id", "container_x", "container_y"]):
await update_container_position(data)
# Для внутреннего вызова используем переводы из WebSocket зависимостии network_data = await get_network_data()
network_data = await get_network_data(translations=translations) logger.info(f"Текущие сетевые данные: {network_data}")
logger.info(f"Current network data: {network_data}")
await manager.broadcast(network_data) await manager.broadcast(network_data)
await asyncio.sleep(2) await asyncio.sleep(2)
except WebSocketDisconnect: except WebSocketDisconnect:
logger.info("Client disconnected") logger.info("Клиент отключен")
except Exception as e: except Exception as e:
logger.error(f"Error in websocket_endpoint: {e}") logger.error(f"Ошибка в websocket_endpoint:{e}")
finally: finally:
manager.disconnect(websocket) manager.disconnect(websocket)
if websocket.client_state != WebSocketState.DISCONNECTED: if websocket.client_state != WebSocketState.DISCONNECTED:
await websocket.close() await websocket.close()
@router.on_event("startup") @router.on_event("startup")
async def startup_event(): async def startup_event():
"""Запуск таска для трансляции сетевых данных при старте приложения. """Запуск таска для трансляции сетевых данных"""
Создаёт асинхронную задачу для периодической трансляции данных.
Raises:
Exception: Если произошла ошибка при запуске таска.
"""
asyncio.create_task(broadcast_data()) asyncio.create_task(broadcast_data())
async def broadcast_data(translations: Translations = None):
"""Таск для периодической трансляции сетевых данных.
Выполняет сканирование сети и отправляет данные всем подключённым клиентам async def broadcast_data():
с интервалом в 2 секунды. """Таск для трансляции сетевых данных"""
Args:
translations: Объект переводов для текущего языка.
Raises:
Exception: Если произошла ошибка при получении данных или трансляции.
"""
translations = translations or _load_translations("ru")
while True: while True:
try: try:
network_data = await get_network_data(translations=translations) network_data = await get_network_data()
if "activation" not in network_data: if "activation" not in network_data:
logger.error(translations.gettext("Ошибка: Ключ 'activation' отсутствует в полученных данных сети")) logger.error(
"Ошибка: Ключ 'activation' отсутствует в полученных данных сети"
)
await asyncio.sleep(2) await asyncio.sleep(2)
continue continue
await manager.broadcast(network_data) await manager.broadcast(network_data)
except Exception as e: except Exception as e:
logger.error(f"Error in broadcast_data: {e}") logger.error(f"Ошибка в broadcast_data:{e}")
await asyncio.sleep(2) await asyncio.sleep(2)

View file

@ -1,3 +0,0 @@
[python: **.py]
encoding = utf-8
extract_messages = _

View file

@ -1,20 +1,3 @@
"""
Конфигурация CORS для FastAPI-приложения GONETS.
Модуль инкапсулирует добавление middleware `CORSMiddleware` со списком
разрешённых источников (origins), характерных для локальной разработки и
типовых портов фронтенда (например, Vite/React на 5173/5174, dev-серверы).
Использование:
cors_config = CORSMiddlewareConfig(app)
cors_config.add_cors_middleware()
Поведение:
- Разрешены все методы (`allow_methods=["*"]`) и заголовки (`allow_headers=["*"]`).
- Разрешены креденшлы (`allow_credentials=True`) для работы с куками (локаль и др.).
- При необходимости список `origins` можно расширить или параметризовать
на основе конфигурационного файла окружения.
"""
from fastapi import FastAPI from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware from starlette.middleware.cors import CORSMiddleware

View file

@ -1,3 +1,4 @@
# init_config.py
import configparser import configparser
import os import os
import secrets import secrets
@ -8,18 +9,7 @@ logger = logging.getLogger(__name__)
def init_config(): def init_config():
""" """Создаёт конфигурационный файл с SECRET_KEY"""
Инициализация конфигурации приложения (JWT SECRET_KEY).
Назначение:
- Создаёт файл `/etc/gonets/config.ini` с секцией `[JWT]` и случайным
`secret_key`, если он отсутствует. Устанавливает права `0o600`.
Замечания:
- Функция предназначена для однократного запуска (в инсталляторе/скрипте
подготовки окружения) и безопасно пропускает работу, если файл уже существует.
"""
"""Создаёт конфигурационный файл с SECRET_KEY."""
config_path = "/etc/gonets/config.ini" config_path = "/etc/gonets/config.ini"
config_dir = os.path.dirname(config_path) config_dir = os.path.dirname(config_path)
@ -44,6 +34,6 @@ def init_config():
) )
raise raise
# Нативный запуск для тестов
if __name__ == "__main__": if __name__ == "__main__":
init_config() init_config()

View file

@ -2,13 +2,7 @@ from typing import Dict, List
class PortConfig: class PortConfig:
"""Конфигурация известных портов для сетевого анализа. """Конфигурация портов для сетевого сканирования."""
Назначение:
Предоставляет сопоставление распространённых портов с человекочитаемыми
названиями сервисов для отображения в UI и логах. Поддерживает расширение
пользовательским словарём.
"""
_DEFAULT_PORTS: Dict[int, str] = { _DEFAULT_PORTS: Dict[int, str] = {
20: "FTP (Data)", 20: "FTP (Data)",

View file

@ -3,11 +3,8 @@ from typing import Tuple
class CLIExecutor: class CLIExecutor:
"""Утилита для безопасного выполнения CLI-команд. """
Класс для выполнения CLI-команд
Назначение:
Инкапсулирует вызов `subprocess.run` и приводит результат к удобному
кортежу (код возврата, stdout) без генерации исключений при ошибках.
""" """
@staticmethod @staticmethod

View file

@ -1,2 +0,0 @@
[JWT]
secret_key = sphinx-docs-secret

View file

@ -1 +0,0 @@
{"sessions": {}}

View file

@ -1 +0,0 @@
{"users": [], "next_id": 1}

View file

@ -1,4 +0,0 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 0de2fdeef2a39cc42113efadfd5a855d
tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,513 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.auth - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.auth</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">Depends</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Request</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">fastapi.security</span><span class="w"> </span><span class="kn">import</span> <span class="n">OAuth2PasswordBearer</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.auth_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">AuthService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.auth</span><span class="w"> </span><span class="kn">import</span> <span class="n">UserCreate</span><span class="p">,</span> <span class="n">UserLogin</span><span class="p">,</span> <span class="n">UserResponse</span><span class="p">,</span> <span class="n">Token</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">storage.json_storage</span><span class="w"> </span><span class="kn">import</span> <span class="n">JsonStorage</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span><span class="p">,</span> <span class="n">_</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">auth_service</span> <span class="o">=</span> <span class="n">AuthService</span><span class="p">()</span>
<span class="n">oauth2_scheme</span> <span class="o">=</span> <span class="n">OAuth2PasswordBearer</span><span class="p">(</span><span class="n">tokenUrl</span><span class="o">=</span><span class="s2">&quot;/auth/login&quot;</span><span class="p">)</span>
<div class="viewcode-block" id="register">
<a class="viewcode-back" href="../../../api/endpoints/auth.html#api.v1.auth.register">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/register&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">UserResponse</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">register</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">UserCreate</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Регистрирует нового пользователя.</span>
<span class="sd"> Создаёт нового пользователя на основе предоставленных данных и возвращает</span>
<span class="sd"> информацию о зарегистрированном пользователе.</span>
<span class="sd"> Args:</span>
<span class="sd"> user (UserCreate): Объект с данными нового пользователя (username, password, is_admin).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> UserResponse: Объект с данными зарегистрированного пользователя (id, username, is_admin).</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если пользователь уже существует (status_code=400) или произошла</span>
<span class="sd"> внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">db_user</span> <span class="o">=</span> <span class="n">auth_service</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Пользователь </span><span class="si">{</span><span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s2"> успешно зарегистрирован&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">db_user</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in register: </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> - </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось зарегистрировать пользователя&quot;</span><span class="p">))</span></div>
<div class="viewcode-block" id="login">
<a class="viewcode-back" href="../../../api/endpoints/auth.html#api.v1.auth.login">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/login&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">Token</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">login</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">UserLogin</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Авторизует пользователя и создаёт сессию.</span>
<span class="sd"> Проверяет учетные данные пользователя и возвращает токен и session_id для</span>
<span class="sd"> последующей авторизации.</span>
<span class="sd"> Args:</span>
<span class="sd"> user (UserLogin): Объект с данными для входа (username, password).</span>
<span class="sd"> request (Request): Объект запроса для получения client_id (IP-адрес клиента).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Token: Объект с access_token и session_id для авторизации.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если учетные данные неверны (status_code=401) или произошла</span>
<span class="sd"> внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">client_id</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">host</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Попытка входа в систему клиента </span><span class="si">{</span><span class="n">client_id</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">auth_service</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="n">client_id</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Успешная авторизация для клиента </span><span class="si">{</span><span class="n">client_id</span><span class="si">}</span><span class="s2">, Идентификатор сессии: </span><span class="si">{</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;session_id&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Ошибка при авторизации: </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> - </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось авторизовать пользователя&quot;</span><span class="p">))</span></div>
<div class="viewcode-block" id="get_current_user">
<a class="viewcode-back" href="../../../api/endpoints/auth.html#api.v1.auth.get_current_user">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_current_user</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Возвращает данные текущего пользователя и токен.</span>
<span class="sd"> Проверяет активную сессию по client_id и возвращает информацию о пользователе</span>
<span class="sd"> и токен для дальнейшего использования.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (Request): Объект запроса для получения client_id (IP-адрес клиента).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами &quot;user&quot; (UserResponse), &quot;access_token&quot; (str) и</span>
<span class="sd"> &quot;token_type&quot; (str, всегда &quot;bearer&quot;).</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если сессия не найдена или истекла (status_code=401) или</span>
<span class="sd"> произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">client_id</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">host</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Запрос /me для клиента </span><span class="si">{</span><span class="n">client_id</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">auth_service</span><span class="o">.</span><span class="n">get_user_by_session</span><span class="p">(</span><span class="n">client_id</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;user&quot;</span><span class="p">:</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;user&quot;</span><span class="p">],</span>
<span class="s2">&quot;access_token&quot;</span><span class="p">:</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;access_token&quot;</span><span class="p">],</span>
<span class="s2">&quot;token_type&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;bearer&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Ошибка при get_current_user: </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> - </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось получить данные пользователя&quot;</span><span class="p">))</span></div>
<div class="viewcode-block" id="logout">
<a class="viewcode-back" href="../../../api/endpoints/auth.html#api.v1.auth.logout">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/logout&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">logout</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Выход из системы, удаление сессии.</span>
<span class="sd"> Удаляет активную сессию пользователя по client_id.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (Request): Объект запроса для получения client_id (IP-адрес клиента).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Пустой словарь или сообщение об успешном выходе.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если сессия не найдена (status_code=401) или произошла</span>
<span class="sd"> внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">client_id</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">host</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Запрос на выход из системы для клиента </span><span class="si">{</span><span class="n">client_id</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">auth_service</span><span class="o">.</span><span class="n">logout</span><span class="p">(</span><span class="n">client_id</span><span class="p">)</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Не удалось выйти из системы: </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> - </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось выйти из системы&quot;</span><span class="p">))</span></div>
<div class="viewcode-block" id="check_users_file">
<a class="viewcode-back" href="../../../api/endpoints/auth.html#api.v1.auth.check_users_file">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/check_json&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">check_users_file</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Проверяет, существует ли файл users.json и содержит ли он пользователей.</span>
<span class="sd"> Возвращает информацию о состоянии файла и наличии пользователей.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами:</span>
<span class="sd"> - &quot;exists&quot; (bool): Существует ли файл.</span>
<span class="sd"> - &quot;has_users&quot; (bool): Содержит ли файл пользователей.</span>
<span class="sd"> - &quot;message&quot; (str): Сообщение с описанием результата.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка при проверке файла (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">file_path</span> <span class="o">=</span> <span class="s2">&quot;/etc/gonets/users.json&quot;</span>
<span class="n">storage</span> <span class="o">=</span> <span class="n">JsonStorage</span><span class="p">(</span><span class="n">file_path</span><span class="o">=</span><span class="n">file_path</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">file_path</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Файл </span><span class="si">{</span><span class="n">file_path</span><span class="si">}</span><span class="s2"> не существует&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;exists&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;has_users&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Файл users.json не существует&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">storage</span><span class="o">.</span><span class="n">_read_data</span><span class="p">()</span>
<span class="n">has_users</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;users&quot;</span><span class="p">,</span> <span class="p">[]))</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">has_users</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Файл </span><span class="si">{</span><span class="n">file_path</span><span class="si">}</span><span class="s2"> существует и содержит пользователей&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;exists&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;has_users&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Файл users.json существует и содержит пользователей&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Файл </span><span class="si">{</span><span class="n">file_path</span><span class="si">}</span><span class="s2"> существует, но не содержит пользователей&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;exists&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;has_users&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Файл users.json существует, но не содержит пользователей&quot;</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Ошибка при проверке файла: </span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2"> - </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка при проверке файла&quot;</span><span class="p">))</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,435 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.ip - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.ip</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Any</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">Body</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Depends</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.ip_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">IPService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">ip_service</span> <span class="o">=</span> <span class="n">IPService</span><span class="p">()</span>
<div class="viewcode-block" id="check_ip_config">
<a class="viewcode-back" href="../../../api/endpoints/ip.html#api.v1.ip.check_ip_config">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/check_ip&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">check_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Проверяет доступность IP-адреса перед его применением.</span>
<span class="sd"> Выполняет ARP-сканирование для проверки, занят ли указанный IP-адрес,</span>
<span class="sd"> и возвращает результат проверки.</span>
<span class="sd"> Args:</span>
<span class="sd"> data (Dict[str, Any]): Словарь с данными конфигурации, включая</span>
<span class="sd"> &quot;ipv4_updates&quot; с полем &quot;ipv4.address-data&quot; для нового IP-адреса.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Dict[str, Any]: Словарь с ключами:</span>
<span class="sd"> - &quot;available&quot; (bool): Доступен ли IP-адрес.</span>
<span class="sd"> - &quot;message&quot; (str): Сообщение о результате проверки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ip_service</span><span class="o">.</span><span class="n">check_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка проверки IP конфигурации: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="apply_ip_config">
<a class="viewcode-back" href="../../../api/endpoints/ip.html#api.v1.ip.apply_ip_config">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/apply_ip&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">apply_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Применяет проверенные IP-настройки к указанному сетевому профилю.</span>
<span class="sd"> Обновляет настройки соединения через NetworkManager, отключает и</span>
<span class="sd"> переподключает устройство к сети.</span>
<span class="sd"> Args:</span>
<span class="sd"> data (Dict[str, Any]): Словарь с данными конфигурации, включая</span>
<span class="sd"> &quot;connection_id&quot; и &quot;ipv4_updates&quot; с обновлениями IP-настроек.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами:</span>
<span class="sd"> - &quot;success&quot; (bool): Успешность операции.</span>
<span class="sd"> - &quot;message&quot; (str): Сообщение об успешном применении настроек.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если подключение не найдено (status_code=404) или</span>
<span class="sd"> произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">await</span> <span class="n">ip_service</span><span class="o">.</span><span class="n">apply_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;success&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Настройки сетевого профиля для </span><span class="si">{connection_id}</span><span class="s2"> применены&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">connection_id</span><span class="o">=</span><span class="n">data</span><span class="p">[</span><span class="s2">&quot;connection_id&quot;</span><span class="p">]</span>
<span class="p">),</span>
<span class="p">}</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка проверки IP конфигурации: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="save_ip_config">
<a class="viewcode-back" href="../../../api/endpoints/ip.html#api.v1.ip.save_ip_config">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/save_ip&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">save_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Применяет проверенные IP-настройки для отката сетевого профиля.</span>
<span class="sd"> Используется для сохранения и отката настроек сетевого профиля к предыдущему</span>
<span class="sd"> состоянию через NetworkManager.</span>
<span class="sd"> Args:</span>
<span class="sd"> data (Dict[str, Any]): Словарь с данными конфигурации, включая</span>
<span class="sd"> &quot;connection_id&quot; и &quot;ipv4_updates&quot; с обновлениями IP-настроек.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами:</span>
<span class="sd"> - &quot;success&quot; (bool): Успешность операции.</span>
<span class="sd"> - &quot;message&quot; (str): Сообщение об успешном применении настроек.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если подключение не найдено (status_code=404) или</span>
<span class="sd"> произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">await</span> <span class="n">ip_service</span><span class="o">.</span><span class="n">apply_ip_config</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;success&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Настройки сетевого профиля для </span><span class="si">{connection_id}</span><span class="s2"> применены&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
<span class="n">connection_id</span><span class="o">=</span><span class="n">data</span><span class="p">[</span><span class="s2">&quot;connection_id&quot;</span><span class="p">]</span>
<span class="p">),</span>
<span class="p">}</span>
<span class="k">except</span> <span class="ne">ValueError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка проверки IP конфигурации: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">)</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,558 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.network - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.network</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">Body</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Depends</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Union</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.network_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">NetworkService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.network</span><span class="w"> </span><span class="kn">import</span> <span class="p">(</span>
<span class="n">NetworkInterfacesContainer</span><span class="p">,</span>
<span class="n">Activation</span><span class="p">,</span>
<span class="n">Deactivation</span><span class="p">,</span>
<span class="n">NetworkInterface</span><span class="p">,</span>
<span class="n">WiFiDevice</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span><span class="p">,</span> <span class="n">_</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">network_service</span> <span class="o">=</span> <span class="n">NetworkService</span><span class="p">()</span>
<div class="viewcode-block" id="ContainerPositionManager">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.ContainerPositionManager">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">ContainerPositionManager</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Класс для управления позициями и состоянием контейнеров.</span>
<span class="sd"> Хранит и обновляет координаты (x, y) и состояние (isOpen) для контейнеров</span>
<span class="sd"> активации и деактивации.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Инициализирует менеджер позиций контейнеров.</span>
<span class="sd"> Создаёт начальные позиции и состояния для контейнеров &quot;activation&quot; и</span>
<span class="sd"> &quot;deactivation&quot; с координатами (0, 0) и закрытым состоянием (False).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">positions</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]]]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;activation&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;x&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s2">&quot;y&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s2">&quot;isOpen&quot;</span><span class="p">:</span> <span class="kc">False</span>
<span class="p">},</span>
<span class="s2">&quot;deactivation&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;x&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s2">&quot;y&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="s2">&quot;isOpen&quot;</span><span class="p">:</span> <span class="kc">False</span>
<span class="p">},</span>
<span class="p">}</span>
<div class="viewcode-block" id="ContainerPositionManager.get_position_and_state">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.ContainerPositionManager.get_position_and_state">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">get_position_and_state</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">()))</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Получает текущие координаты и состояние указанного контейнера.</span>
<span class="sd"> Args:</span>
<span class="sd"> container_name (str): Имя контейнера (&quot;activation&quot; или &quot;deactivation&quot;).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Tuple[int, int, bool]: Кортеж с координатами (x, y) и состоянием (isOpen).</span>
<span class="sd"> Raises:</span>
<span class="sd"> ValueError: Если указан неверный идентификатор контейнера.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">container</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">positions</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">container_name</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">container</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Неверное имя контейнера: </span><span class="si">{container_name}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">container_name</span><span class="o">=</span><span class="n">container_name</span><span class="p">))</span>
<span class="k">return</span> <span class="n">container</span><span class="p">[</span><span class="s2">&quot;x&quot;</span><span class="p">],</span> <span class="n">container</span><span class="p">[</span><span class="s2">&quot;y&quot;</span><span class="p">],</span> <span class="n">container</span><span class="p">[</span><span class="s2">&quot;isOpen&quot;</span><span class="p">]</span></div>
<div class="viewcode-block" id="ContainerPositionManager.update_position">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.ContainerPositionManager.update_position">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">update_position</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">isOpen</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Обновляет позицию и состояние контейнера по его идентификатору.</span>
<span class="sd"> Args:</span>
<span class="sd"> container_id (int): Идентификатор контейнера (1 для &quot;activation&quot;, 2 для &quot;deactivation&quot;).</span>
<span class="sd"> x (int): Новая координата X.</span>
<span class="sd"> y (int): Новая координата Y.</span>
<span class="sd"> isOpen (bool): Новое состояние контейнера (открыт/закрыт).</span>
<span class="sd"> Raises:</span>
<span class="sd"> ValueError: Если container_id не соответствует ни одному контейнеру.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">container_name</span> <span class="o">=</span> <span class="s2">&quot;activation&quot;</span> <span class="k">if</span> <span class="n">container_id</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">else</span> <span class="s2">&quot;deactivation&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">positions</span><span class="p">[</span><span class="n">container_name</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;x&quot;</span><span class="p">:</span> <span class="n">x</span><span class="p">,</span> <span class="s2">&quot;y&quot;</span><span class="p">:</span> <span class="n">y</span><span class="p">,</span> <span class="s2">&quot;isOpen&quot;</span><span class="p">:</span> <span class="n">isOpen</span><span class="p">}</span></div>
</div>
<span class="n">container_manager</span> <span class="o">=</span> <span class="n">ContainerPositionManager</span><span class="p">()</span>
<div class="viewcode-block" id="get_network_data">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.get_network_data">[docs]</a>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_network_data</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">()))</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Dict</span><span class="p">]:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Возвращает текущие сетевые данные в виде словаря.</span>
<span class="sd"> Получает данные о сетевых интерфейсах, разделяет их на активные и неактивные,</span>
<span class="sd"> и возвращает их в формате Activation и Deactivation с учётом позиций и состояний</span>
<span class="sd"> контейнеров.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Dict[str, Dict]: Словарь с ключами &quot;activation&quot; и &quot;deactivation&quot;, содержащими</span>
<span class="sd"> данные о соответствующих контейнерах.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка при получении данных (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;Calling get_network_data()&quot;</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">network_data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">network_service</span><span class="o">.</span><span class="n">get_network_interfaces_container</span><span class="p">()</span>
<span class="n">activation_interfaces</span><span class="p">,</span> <span class="n">deactivation_interfaces</span> <span class="o">=</span> <span class="n">_split_interfaces</span><span class="p">(</span><span class="n">network_data</span><span class="o">.</span><span class="n">interfaces</span><span class="p">)</span>
<span class="n">activation_x</span><span class="p">,</span> <span class="n">activation_y</span><span class="p">,</span> <span class="n">activation_is_open</span> <span class="o">=</span> <span class="p">(</span><span class="n">container_manager</span><span class="o">.</span><span class="n">get_position_and_state</span><span class="p">(</span><span class="s2">&quot;activation&quot;</span><span class="p">))</span>
<span class="n">deactivation_x</span><span class="p">,</span> <span class="n">deactivation_y</span><span class="p">,</span> <span class="n">deactivation_is_open</span> <span class="o">=</span> <span class="p">(</span><span class="n">container_manager</span><span class="o">.</span><span class="n">get_position_and_state</span><span class="p">(</span><span class="s2">&quot;deactivation&quot;</span><span class="p">))</span>
<span class="n">activation</span> <span class="o">=</span> <span class="n">Activation</span><span class="p">(</span>
<span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
<span class="n">interfaces</span><span class="o">=</span><span class="n">activation_interfaces</span><span class="p">,</span>
<span class="n">container_x</span><span class="o">=</span><span class="n">activation_x</span><span class="p">,</span>
<span class="n">container_y</span><span class="o">=</span><span class="n">activation_y</span><span class="p">,</span>
<span class="n">isOpen</span><span class="o">=</span><span class="n">activation_is_open</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">deactivation</span> <span class="o">=</span> <span class="n">Deactivation</span><span class="p">(</span>
<span class="nb">id</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
<span class="n">interfaces</span><span class="o">=</span><span class="n">deactivation_interfaces</span><span class="p">,</span>
<span class="n">container_x</span><span class="o">=</span><span class="n">deactivation_x</span><span class="p">,</span>
<span class="n">container_y</span><span class="o">=</span><span class="n">deactivation_y</span><span class="p">,</span>
<span class="n">isOpen</span><span class="o">=</span><span class="n">deactivation_is_open</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;activation&quot;</span><span class="p">:</span> <span class="n">activation</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(),</span>
<span class="s2">&quot;deactivation&quot;</span><span class="p">:</span> <span class="n">deactivation</span><span class="o">.</span><span class="n">model_dump</span><span class="p">(),</span>
<span class="p">}</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in get_network_data: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось получить сетевые данные&quot;</span><span class="p">))</span></div>
<span class="k">def</span><span class="w"> </span><span class="nf">_split_interfaces</span><span class="p">(</span>
<span class="n">interfaces</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="n">NetworkInterface</span><span class="p">,</span> <span class="n">WiFiDevice</span><span class="p">]],</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="n">NetworkInterface</span><span class="p">,</span> <span class="n">WiFiDevice</span><span class="p">]],</span> <span class="n">List</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="n">NetworkInterface</span><span class="p">,</span> <span class="n">WiFiDevice</span><span class="p">]]]:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Разделяет интерфейсы на активные и неактивные.</span>
<span class="sd"> Args:</span>
<span class="sd"> interfaces (List[Union[NetworkInterface, WiFiDevice]]): Список сетевых интерфейсов.</span>
<span class="sd"> Returns:</span>
<span class="sd"> Tuple[List[Union[NetworkInterface, WiFiDevice]], List[Union[NetworkInterface, WiFiDevice]]]:</span>
<span class="sd"> Кортеж из списков активных и неактивных интерфейсов.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">activation_interfaces</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">deactivation_interfaces</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">interface</span> <span class="ow">in</span> <span class="n">interfaces</span><span class="p">:</span>
<span class="k">if</span> <span class="n">interface</span><span class="o">.</span><span class="n">device_state</span> <span class="o">==</span> <span class="s2">&quot;100&quot;</span><span class="p">:</span>
<span class="n">activation_interfaces</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">interface</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">deactivation_interfaces</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">interface</span><span class="p">)</span>
<span class="k">return</span> <span class="n">activation_interfaces</span><span class="p">,</span> <span class="n">deactivation_interfaces</span>
<div class="viewcode-block" id="update_container_position">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.update_container_position">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/update_container_position&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_container_position</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Union</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]]</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Обновляет позицию и состояние контейнера.</span>
<span class="sd"> Принимает данные о новом положении и состоянии контейнера и обновляет их</span>
<span class="sd"> в менеджере позиций.</span>
<span class="sd"> Args:</span>
<span class="sd"> data (Dict[str, Union[int, bool]]): Словарь с ключами:</span>
<span class="sd"> - &quot;container_id&quot; (int): Идентификатор контейнера (1 или 2).</span>
<span class="sd"> - &quot;container_x&quot; (int): Новая координата X.</span>
<span class="sd"> - &quot;container_y&quot; (int): Новая координата Y.</span>
<span class="sd"> - &quot;isOpen&quot; (bool): Новое состояние контейнера (открыт/закрыт).</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами:</span>
<span class="sd"> - &quot;success&quot; (bool): Успешность операции.</span>
<span class="sd"> - &quot;message&quot; (str): Сообщение об успешном обновлении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если отсутствуют обязательные поля (status_code=400) или</span>
<span class="sd"> типы данных неверны.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">required_keys</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">&quot;container_id&quot;</span><span class="p">,</span> <span class="s2">&quot;container_x&quot;</span><span class="p">,</span> <span class="s2">&quot;container_y&quot;</span><span class="p">,</span> <span class="s2">&quot;isOpen&quot;</span>
<span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">all</span><span class="p">(</span><span class="n">key</span> <span class="ow">in</span> <span class="n">data</span> <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">required_keys</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Отсутствуют обязательные поля: container_id, container_x, container_y, isOpen&quot;</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">container_id</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s2">&quot;container_id&quot;</span><span class="p">]</span>
<span class="n">container_x</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s2">&quot;container_x&quot;</span><span class="p">]</span>
<span class="n">container_y</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s2">&quot;container_y&quot;</span><span class="p">]</span>
<span class="n">is_open</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s2">&quot;isOpen&quot;</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">container_id</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">container_x</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">container_y</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span>
<span class="ow">or</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">is_open</span><span class="p">,</span> <span class="nb">bool</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Неверный тип данных в запросе&quot;</span><span class="p">))</span>
<span class="n">container_manager</span><span class="o">.</span><span class="n">update_position</span><span class="p">(</span><span class="n">container_id</span><span class="p">,</span> <span class="n">container_x</span><span class="p">,</span> <span class="n">container_y</span><span class="p">,</span> <span class="n">is_open</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;success&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Позиция и состояние контейнера обновлены&quot;</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error updating container position: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось обновить положение контейнера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="get_hostname">
<a class="viewcode-back" href="../../../api/endpoints/network.html#api.v1.network.get_hostname">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/get_hostname&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_hostname</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Возвращает имя хоста системы.</span>
<span class="sd"> Выполняет запрос к сервису для получения имени хоста.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключом &quot;hostname&quot; и значением имени хоста.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка при получении имени хоста (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">hostname_info</span> <span class="o">=</span> <span class="k">await</span> <span class="n">network_service</span><span class="o">.</span><span class="n">get_hostname</span><span class="p">()</span>
<span class="k">return</span> <span class="n">hostname_info</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in get_hostname: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось получить имя хоста&quot;</span><span class="p">))</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,545 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.settings - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.settings</h1><div class="highlight"><pre>
<span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">subprocess</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Optional</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Depends</span><span class="p">,</span> <span class="n">Response</span><span class="p">,</span> <span class="n">Request</span><span class="p">,</span> <span class="n">Body</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.responses</span><span class="w"> </span><span class="kn">import</span> <span class="n">FileResponse</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.settings_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">SettingsService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.subnet</span><span class="w"> </span><span class="kn">import</span> <span class="n">SubnetScanRequest</span> <span class="c1"># Предполагается, что модель определена</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">settings_service</span> <span class="o">=</span> <span class="n">SettingsService</span><span class="p">()</span>
<div class="viewcode-block" id="clear_subnet">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.clear_subnet">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/clear_subnet&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">clear_subnet</span><span class="p">(</span><span class="n">subnet</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Очищает данные узлов подсети, сохранённые в devices.json.</span>
<span class="sd"> Удаляет все записи об узлах подсети, оставляя файл пустым.</span>
<span class="sd"> Args:</span>
<span class="sd"> subnet (Optional[str]): Подсеть в формате CIDR (например, &quot;10.1.1.0/24&quot;).</span>
<span class="sd"> Если None, очищает все подсети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешной очистке.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка очистки (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">settings_service</span><span class="o">.</span><span class="n">clear_subnet</span><span class="p">(</span><span class="n">subnet</span><span class="p">)</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">subnet</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Данные узлов подсети </span><span class="si">{subnet}</span><span class="s2"> успешно очищены&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">subnet</span><span class="o">=</span><span class="n">subnet</span><span class="p">)}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Данные всех подсетей успешно очищены&quot;</span><span class="p">)}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось очистить данные узлов подсети&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error clearing subnet data: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка очистки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="clear_wifi">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.clear_wifi">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/clear_wifi&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">clear_wifi</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Очищает данные точек доступа Wi-Fi, сохранённые в wifi.json.</span>
<span class="sd"> Удаляет все записи о точках доступа, оставляя файл пустым.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешной очистке.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка очистки (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">settings_service</span><span class="o">.</span><span class="n">clear_wifi</span><span class="p">()</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Данные точек доступа Wi-Fi успешно очищены&quot;</span><span class="p">)}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось очистить данные точек доступа Wi-Fi&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error clearing wifi data: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка очистки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="clear_all_data">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.clear_all_data">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/clear_all_data&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">clear_all_data</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Очищает данные узлов подсети и точек доступа Wi-Fi в обоих файлах.</span>
<span class="sd"> Удаляет все записи из devices.json и wifi.json.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешной очистке.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка очистки (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">settings_service</span><span class="o">.</span><span class="n">clear_all_data</span><span class="p">()</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Все сетевые данные успешно очищены&quot;</span><span class="p">)}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось очистить все сетевые данные&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error clearing all data: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка очистки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<span class="n">WIFI_PATH</span> <span class="o">=</span> <span class="s2">&quot;storage/wifi/wifi.json&quot;</span>
<div class="viewcode-block" id="export_devices">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.export_devices">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/export_devices&quot;</span><span class="p">,</span> <span class="n">response_class</span><span class="o">=</span><span class="n">FileResponse</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">export_devices</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">SubnetScanRequest</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Экспортирует файл с данными устройств в формате JSON для указанной подсети.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (SubnetScanRequest): Объект с полем subnet в формате CIDR.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> FileResponse: Файл &lt;subnet&gt;.json для скачивания.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если файл не найден (status_code=404).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">subnet</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">subnet</span>
<span class="n">file_path</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;storage/subnets/</span><span class="si">{</span><span class="n">subnet</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;_&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">.json&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">file_path</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Export file not found: </span><span class="si">{</span><span class="n">file_path</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Файл для подсети </span><span class="si">{subnet}</span><span class="s2"> не найден&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">subnet</span><span class="o">=</span><span class="n">subnet</span><span class="p">))</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Exporting file: </span><span class="si">{</span><span class="n">file_path</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">FileResponse</span><span class="p">(</span>
<span class="n">path</span><span class="o">=</span><span class="n">file_path</span><span class="p">,</span>
<span class="n">filename</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">subnet</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;_&#39;</span><span class="p">)</span><span class="si">}</span><span class="s2">.json&quot;</span><span class="p">,</span>
<span class="n">media_type</span><span class="o">=</span><span class="s2">&quot;application/json&quot;</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="export_wifi">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.export_wifi">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/export_wifi&quot;</span><span class="p">,</span> <span class="n">response_class</span><span class="o">=</span><span class="n">FileResponse</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">export_wifi</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Экспортирует файл с данными устройств в формате JSON.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> FileResponse: Файл wifi.json для скачивания.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если файл не найден (status_code=404).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">WIFI_PATH</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Export file not found: </span><span class="si">{</span><span class="n">WIFI_PATH</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Файл не найден&quot;</span><span class="p">))</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Exporting file: </span><span class="si">{</span><span class="n">WIFI_PATH</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">FileResponse</span><span class="p">(</span>
<span class="n">path</span><span class="o">=</span><span class="n">WIFI_PATH</span><span class="p">,</span>
<span class="n">filename</span><span class="o">=</span><span class="s2">&quot;wifi.json&quot;</span><span class="p">,</span>
<span class="n">media_type</span><span class="o">=</span><span class="s2">&quot;application/json&quot;</span>
<span class="p">)</span></div>
<div class="viewcode-block" id="set_language">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.set_language">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/set_language&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">set_language</span><span class="p">(</span><span class="n">locale</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">response</span><span class="p">:</span> <span class="n">Response</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Устанавливает куку locale для смены языка пользователя.</span>
<span class="sd"> Args:</span>
<span class="sd"> locale (str): Используемый язык.</span>
<span class="sd"> response (Response): Запрос.</span>
<span class="sd"> translations: Объект переводов текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Сообщение об успешной установке языка.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">lc_messages_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s1">&#39;..&#39;</span><span class="p">,</span> <span class="s1">&#39;..&#39;</span><span class="p">,</span> <span class="s1">&#39;locales&#39;</span><span class="p">,</span> <span class="n">locale</span><span class="p">,</span> <span class="s1">&#39;LC_MESSAGES&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">lc_messages_path</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Язык &#39;</span><span class="si">{locale}</span><span class="s2">&#39; не поддерживается.&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">locale</span><span class="o">=</span><span class="n">locale</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">has_mo_or_po</span> <span class="o">=</span> <span class="nb">any</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s1">&#39;.mo&#39;</span><span class="p">)</span> <span class="ow">or</span> <span class="n">f</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s1">&#39;.po&#39;</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">lc_messages_path</span><span class="p">))</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">has_mo_or_po</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Язык &#39;</span><span class="si">{locale}</span><span class="s2">&#39; не поддерживается (нет файлов переводов).&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">locale</span><span class="o">=</span><span class="n">locale</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">response</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s2">&quot;locale&quot;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="n">locale</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">,</span> <span class="n">httponly</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Язык успешно установлен: </span><span class="si">{locale}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">locale</span><span class="o">=</span><span class="n">locale</span><span class="p">)}</span></div>
<div class="viewcode-block" id="get_system_language">
<a class="viewcode-back" href="../../../api/endpoints/settings.html#api.v1.settings.get_system_language">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/get_system_language&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_system_language</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Выводит текущий язык, установленный в системе на основе locale.</span>
<span class="sd"> Выполняет команду &#39;locale&#39; в системе и извлекает параметры LANG</span>
<span class="sd"> Args:</span>
<span class="sd"> request (Request): Объект запроса для доступа к параметрам.</span>
<span class="sd"> translations: Объект переводов текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с текущим языком системы.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если не удалось извлечь системный язык (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">([</span><span class="s1">&#39;locale&#39;</span><span class="p">],</span> <span class="n">capture_output</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">check</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">stdout</span>
<span class="n">lang</span> <span class="o">=</span> <span class="nb">next</span><span class="p">((</span><span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;=&#39;</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">output</span><span class="o">.</span><span class="n">splitlines</span><span class="p">()</span> <span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;LANG=&#39;</span><span class="p">)),</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">lang</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;language&quot;</span><span class="p">:</span> <span class="n">lang</span><span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;No locale found: </span><span class="si">{</span><span class="n">output</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;language&quot;</span><span class="p">:</span> <span class="s1">&#39;unknown&#39;</span><span class="p">}</span>
<span class="k">except</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">CalledProcessError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error executing locale command: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Не удалось определить язык в системе&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Unexpected error getting system language: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка при определении языка в системе </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,700 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.subnet - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.subnet</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">HTTPException</span><span class="p">,</span> <span class="n">Body</span><span class="p">,</span> <span class="n">Depends</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.subnet</span><span class="w"> </span><span class="kn">import</span> <span class="n">SubnetScanResult</span><span class="p">,</span> <span class="n">SubnetScanRequest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.wifi</span><span class="w"> </span><span class="kn">import</span> <span class="n">WifiScanResult</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.request</span><span class="w"> </span><span class="kn">import</span> <span class="n">NoteRequest</span><span class="p">,</span> <span class="n">WifiConnectRequest</span><span class="p">,</span> <span class="n">WifiDisconnectRequest</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.subnet_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">SubnetService</span><span class="p">,</span> <span class="n">get_subnet_service</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.wifi_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">WifiService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.connect_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">WifiConnectionService</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span><span class="p">,</span> <span class="n">_</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<div class="viewcode-block" id="scan_subnet">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.scan_subnet">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/devices&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">SubnetScanResult</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">scan_subnet</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">SubnetScanRequest</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="n">subnet_service</span><span class="p">:</span> <span class="n">SubnetService</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_subnet_service</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Сканирует локальную подсеть и возвращает данные об устройствах.</span>
<span class="sd"> Выполняет сканирование указанной подсети для обнаружения активных устройств.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (SubnetScanRequest): Объект с полем subnet.</span>
<span class="sd"> subnet_service: Сервис сканирования подсети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> SubnetScanResult: Объект с результатами сканирования (устройства и их данные).</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка сканирования (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># Если subnet пустая строка, определяем автоматически</span>
<span class="n">subnet</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">subnet</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">subnet</span> <span class="k">else</span> <span class="kc">None</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Requested subnet scan for: </span><span class="si">{</span><span class="n">subnet</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="s1">&#39;auto-detected&#39;</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">subnet_service</span><span class="o">.</span><span class="n">scan_subnet</span><span class="p">(</span><span class="n">subnet</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">result</span><span class="o">.</span><span class="n">devices</span> <span class="ow">and</span> <span class="n">result</span><span class="o">.</span><span class="n">error</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;No devices found in subnet </span><span class="si">{</span><span class="n">subnet</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="s1">&#39;auto-detected&#39;</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in scan_subnet: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка сканирования подсети: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="scan_wifi">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.scan_wifi">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/wifi&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">WifiScanResult</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">scan_wifi</span><span class="p">(</span><span class="n">timeout</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">30</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Сканирует Wi-Fi точки доступа и возвращает данные о них.</span>
<span class="sd"> Выполняет сканирование доступных Wi-Fi сетей с указанным таймаутом.</span>
<span class="sd"> Args:</span>
<span class="sd"> timeout (int, optional): Таймаут сканирования в секундах. По умолчанию 30.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> WifiScanResult: Объект с результатами сканирования (список Wi-Fi сетей).</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если произошла ошибка сканирования Wi-Fi (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiService</span><span class="p">()</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">scan_wifi</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка сканирования Wi-Fi: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="add_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.add_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/devices/</span><span class="si">{mac}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">add_note</span><span class="p">(</span><span class="n">mac</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">NoteRequest</span><span class="p">,</span> <span class="n">subnet_service</span><span class="p">:</span> <span class="n">SubnetService</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_subnet_service</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Добавляет заметку для устройства по MAC-адресу.</span>
<span class="sd"> Привязывает заметку к устройству, идентифицируемому по MAC-адресу.</span>
<span class="sd"> Args:</span>
<span class="sd"> mac (str): MAC-адрес устройства.</span>
<span class="sd"> request (NoteRequest): Объект с полем &quot;note&quot; для текста заметки.</span>
<span class="sd"> subnet_service: Сервис сканирования подсети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном добавлении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если устройство не найдено (status_code=404), заметка уже</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subnet_service</span><span class="o">.</span><span class="n">add_note</span><span class="p">(</span><span class="n">mac</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Устройство с MAC </span><span class="si">{mac}</span><span class="s2"> не найдено&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для устройства с MAC </span><span class="si">{mac}</span><span class="s2"> уже существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка &#39;</span><span class="si">{note}</span><span class="s2">&#39; добавлена для устройства </span><span class="si">{mac}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">note</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка добавления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="update_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.update_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s2">&quot;/devices/</span><span class="si">{mac}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">update_note</span><span class="p">(</span><span class="n">mac</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">NoteRequest</span><span class="p">,</span> <span class="n">subnet_service</span><span class="p">:</span> <span class="n">SubnetService</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_subnet_service</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Обновляет заметку для устройства по MAC-адресу.</span>
<span class="sd"> Изменяет существующую заметку, привязанную к устройству.</span>
<span class="sd"> Args:</span>
<span class="sd"> mac (str): MAC-адрес устройства.</span>
<span class="sd"> request (NoteRequest): Объект с полем &quot;note&quot; для нового текста заметки.</span>
<span class="sd"> subnet_service: Сервис сканирования подсети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном обновлении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если устройство не найдено (status_code=404), заметка не</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subnet_service</span><span class="o">.</span><span class="n">update_note</span><span class="p">(</span><span class="n">mac</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Устройство с MAC </span><span class="si">{mac}</span><span class="s2"> не найдено&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_not_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для устройства с MAC </span><span class="si">{mac}</span><span class="s2"> не существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка &#39;</span><span class="si">{note}</span><span class="s2">&#39; обновлена для устройства </span><span class="si">{mac}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">note</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка обновления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="delete_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.delete_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&quot;/devices/</span><span class="si">{mac}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">delete_note</span><span class="p">(</span><span class="n">mac</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">subnet_service</span><span class="p">:</span> <span class="n">SubnetService</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_subnet_service</span><span class="p">),</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Удаляет заметку для устройства по MAC-адресу.</span>
<span class="sd"> Удаляет привязанную заметку для указанного устройства.</span>
<span class="sd"> Args:</span>
<span class="sd"> mac (str): MAC-адрес устройства.</span>
<span class="sd"> subnet_service: Сервис сканирования подсети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном удалении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если устройство не найдено (status_code=404), заметка не</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">subnet_service</span><span class="o">.</span><span class="n">delete_note</span><span class="p">(</span><span class="n">mac</span><span class="p">,</span> <span class="n">translations</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Устройство с MAC </span><span class="si">{mac}</span><span class="s2"> не найдено&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_not_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для устройства с MAC </span><span class="si">{mac}</span><span class="s2"> не существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка удалена для устройства </span><span class="si">{mac}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">mac</span><span class="o">=</span><span class="n">mac</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка удаления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="add_wifi_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.add_wifi_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/wifi/</span><span class="si">{ssid}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">add_wifi_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">NoteRequest</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Добавляет заметку для сети Wi-Fi по SSID.</span>
<span class="sd"> Привязывает заметку к Wi-Fi сети, идентифицируемой по SSID.</span>
<span class="sd"> Args:</span>
<span class="sd"> ssid (str): Идентификатор Wi-Fi сети.</span>
<span class="sd"> request (NoteRequest): Объект с полем &quot;note&quot; для текста заметки.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном добавлении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если сеть не найдена (status_code=404), заметка уже</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiService</span><span class="p">()</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">add_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Сеть с SSID </span><span class="si">{ssid}</span><span class="s2"> не найдена&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для сети с SSID </span><span class="si">{ssid}</span><span class="s2"> уже существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка &#39;</span><span class="si">{note}</span><span class="s2">&#39; добавлена для сети с SSID </span><span class="si">{ssid}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">note</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка добавления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="update_wifi_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.update_wifi_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="s2">&quot;/wifi/</span><span class="si">{ssid}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">update_wifi_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">NoteRequest</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Обновляет заметку для сети Wi-Fi по SSID.</span>
<span class="sd"> Изменяет существующую заметку, привязанную к Wi-Fi сети.</span>
<span class="sd"> Args:</span>
<span class="sd"> ssid (str): Идентификатор Wi-Fi сети.</span>
<span class="sd"> request (NoteRequest): Объект с полем &quot;note&quot; для нового текста заметки.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном обновлении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если сеть не найдена (status_code=404), заметка не</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiService</span><span class="p">()</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">update_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Сеть с SSID </span><span class="si">{ssid}</span><span class="s2"> не найдена&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_not_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для сети с SSID </span><span class="si">{ssid}</span><span class="s2"> не существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка &#39;</span><span class="si">{note}</span><span class="s2">&#39; обновлена для сети с SSID </span><span class="si">{ssid}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">note</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">note</span><span class="p">,</span> <span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка обновления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="delete_wifi_note">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.delete_wifi_note">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&quot;/wifi/</span><span class="si">{ssid}</span><span class="s2">/note&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">delete_wifi_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Удаляет заметку для сети Wi-Fi по SSID.</span>
<span class="sd"> Удаляет привязанную заметку для указанной Wi-Fi сети.</span>
<span class="sd"> Args:</span>
<span class="sd"> ssid (str): Идентификатор Wi-Fi сети.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном удалении заметки.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если сеть не найдена (status_code=404), заметка не</span>
<span class="sd"> существует (status_code=400), или произошла внутренняя ошибка (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiService</span><span class="p">()</span>
<span class="n">success</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">delete_note</span><span class="p">(</span><span class="n">ssid</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;not_found&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Сеть с SSID </span><span class="si">{ssid}</span><span class="s2"> не найдена&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">elif</span> <span class="n">result</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&quot;note_not_exists&quot;</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка для сети с SSID </span><span class="si">{ssid}</span><span class="s2"> не существует&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Внутренняя ошибка сервера: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="mi">1</span><span class="p">][</span><span class="s2">&quot;error&quot;</span><span class="p">]))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Заметка удалена для сети с SSID </span><span class="si">{ssid}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">ssid</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка удаления заметки: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="connect_to_wifi">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.connect_to_wifi">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/wifi/connect&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">connect_to_wifi</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">WifiConnectRequest</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Подключается к Wi-Fi сети по SSID и паролю.</span>
<span class="sd"> Выполняет подключение к указанной Wi-Fi сети с использованием предоставленных</span>
<span class="sd"> учетных данных.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (WifiConnectRequest): Объект с полями &quot;ssid&quot; и &quot;password&quot;.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном подключении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если подключение не удалось (status_code=400).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiConnectionService</span><span class="p">(</span><span class="n">translations</span><span class="p">)</span>
<span class="n">success</span><span class="p">,</span> <span class="n">message</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">ssid</span><span class="p">,</span> <span class="n">request</span><span class="o">.</span><span class="n">password</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="n">message</span><span class="p">))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Успешно подключено к </span><span class="si">{ssid}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">ssid</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка подключения к Wi-Fi: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="disconnect_from_wifi">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.disconnect_from_wifi">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/wifi/disconnect&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">disconnect_from_wifi</span><span class="p">(</span><span class="n">request</span><span class="p">:</span> <span class="n">WifiDisconnectRequest</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Отключается от Wi-Fi сети по указанному SSID.</span>
<span class="sd"> Выполняет отключение от указанной Wi-Fi сети.</span>
<span class="sd"> Args:</span>
<span class="sd"> request (WifiDisconnectRequest): Объект с полем &quot;ssid&quot;.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с сообщением об успешном отключении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если отключение не удалось (status_code=400).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiConnectionService</span><span class="p">(</span><span class="n">translations</span><span class="p">)</span>
<span class="n">success</span><span class="p">,</span> <span class="n">message</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">ssid</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="n">message</span><span class="p">))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="n">message</span> <span class="ow">or</span> <span class="s2">&quot;Успешно отключено от </span><span class="si">{ssid}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ssid</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">ssid</span><span class="p">)}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка отключения от Wi-Fi: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
<div class="viewcode-block" id="get_wifi_status">
<a class="viewcode-back" href="../../../api/endpoints/subnet.html#api.v1.subnet.get_wifi_status">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/wifi/status&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get_wifi_status</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Проверяет статус текущего подключения к Wi-Fi.</span>
<span class="sd"> Возвращает информацию о текущем состоянии подключения и SSID, если подключение активно.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами:</span>
<span class="sd"> - &quot;status&quot; (str): Состояние (&quot;connected&quot; или другое).</span>
<span class="sd"> - &quot;ssid&quot; (str, optional): SSID текущей сети, если подключено.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если проверка статуса не удалась (status_code=400).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">wifi_service</span> <span class="o">=</span> <span class="n">WifiConnectionService</span><span class="p">(</span><span class="n">translations</span><span class="p">)</span>
<span class="n">success</span><span class="p">,</span> <span class="n">message</span> <span class="o">=</span> <span class="n">wifi_service</span><span class="o">.</span><span class="n">get_status</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">success</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="n">message</span><span class="p">))</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;connected&quot;</span><span class="p">),</span> <span class="s2">&quot;ssid&quot;</span><span class="p">:</span> <span class="n">message</span><span class="p">}</span>
<span class="k">except</span> <span class="n">HTTPException</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">e</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка проверки статуса Wi-Fi: </span><span class="si">{error}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">error</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,555 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.ufw - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.ufw</h1><div class="highlight"><pre>
<span></span><span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">HTTPException</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">List</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">models.ufw</span><span class="w"> </span><span class="kn">import</span> <span class="n">UFWRulesResponse</span><span class="p">,</span> <span class="n">UFWStatus</span><span class="p">,</span> <span class="n">UFWRule</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">services.ufw_service</span><span class="w"> </span><span class="kn">import</span> <span class="n">UFWService</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<div class="viewcode-block" id="RuleNumbers">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.RuleNumbers">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">RuleNumbers</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Модель запроса на удаление UFW правил.</span>
<span class="sd"> Атрибуты:</span>
<span class="sd"> - ``numbers`` (List[int]): Список номеров правил для удаления.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">numbers</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span>
<span class="o">...</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Список номеров правил для удаления&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="get_ufw_rules">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.get_ufw_rules">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/rules&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">UFWRulesResponse</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_ufw_rules</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Возвращает все правила UFW.</span>
<span class="sd"> Осуществляет обращение к сервису `UFWService` для чтения текущего списка</span>
<span class="sd"> правил брандмауэра и возвращает результат в сериализованном виде для клиента.</span>
<span class="sd"> Returns:</span>
<span class="sd"> UFWRulesResponse: Структура с коллекцией правил и сопутствующей</span>
<span class="sd"> информацией о состоянии.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: В случае ошибок взаимодействия с UFW (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">get_ufw_rules</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="get_ufw_status">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.get_ufw_status">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/status&quot;</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">UFWStatus</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_ufw_status</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Возвращает текущий статус UFW.</span>
<span class="sd"> Returns:</span>
<span class="sd"> UFWStatus: Объект со сведениями о включенности UFW и деталями статуса.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если не удалось получить статус (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">get_ufw_status</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="enable_ufw">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.enable_ufw">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/enable&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">enable_ufw</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Включает UFW.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами `status` и `message` при успешном выполнении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если включение завершилось неуспешно (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">enable_ufw</span><span class="p">()</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span> <span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="s2">&quot;UFW успешно включен&quot;</span><span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="s2">&quot;Не удалось включить UFW&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="disable_ufw">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.disable_ufw">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/disable&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">disable_ufw</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Выключает UFW.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Словарь с ключами `status` и `message` при успешном выполнении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если выключение завершилось неуспешно (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">disable_ufw</span><span class="p">()</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span> <span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="s2">&quot;UFW успешно выключен&quot;</span><span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="s2">&quot;Не удалось выключить UFW&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="add_ufw_in_rules">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.add_ufw_in_rules">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/rules/in&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_ufw_in_rules</span><span class="p">(</span><span class="n">rules</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">UFWRule</span><span class="p">]):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Добавляет входящие (in) правила UFW.</span>
<span class="sd"> Args:</span>
<span class="sd"> rules (List[UFWRule]): Список правил, определяющих направления,</span>
<span class="sd"> порты/диапазоны/протоколы и действия (allow/deny/reject).</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Итог выполнения с количеством успешно добавленных правил и деталями</span>
<span class="sd"> по каждому правилу.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если UFW отключен (status_code=400) или при внутренних</span>
<span class="sd"> ошибках (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># Проверяем статус UFW</span>
<span class="n">status</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">get_ufw_status</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">status</span><span class="o">.</span><span class="n">enabled</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="s2">&quot;UFW отключен. Включите UFW перед добавлением правил.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">add_rules</span><span class="p">(</span><span class="n">rules</span><span class="p">)</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;success&quot;</span><span class="p">]:</span>
<span class="n">successful_rules</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span>
<span class="p">[</span><span class="n">r</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;results&quot;</span><span class="p">]</span> <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="s2">&quot;success&quot;</span><span class="p">]])</span>
<span class="n">total_rules</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">[</span><span class="s2">&quot;results&quot;</span><span class="p">])</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span>
<span class="sa">f</span><span class="s2">&quot;Добавлено </span><span class="si">{</span><span class="n">successful_rules</span><span class="si">}</span><span class="s2"> из </span><span class="si">{</span><span class="n">total_rules</span><span class="si">}</span><span class="s2"> правил&quot;</span><span class="p">,</span>
<span class="s2">&quot;details&quot;</span><span class="p">:</span> <span class="n">result</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="n">result</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;error&quot;</span><span class="p">,</span> <span class="s2">&quot;Не удалось добавить правила&quot;</span><span class="p">),</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="add_ufw_out_rules">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.add_ufw_out_rules">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/rules/out&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_ufw_out_rules</span><span class="p">(</span><span class="n">rules</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">UFWRule</span><span class="p">]):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Добавляет исходящие (out) правила UFW.</span>
<span class="sd"> Args:</span>
<span class="sd"> rules (List[UFWRule]): Список правил исходящего трафика для добавления.</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Объект статуса с сообщением об успешном добавлении.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: Если UFW отключен (status_code=400) или при внутренних</span>
<span class="sd"> ошибках (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># Проверяем статус UFW</span>
<span class="n">status</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">get_ufw_status</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">status</span><span class="o">.</span><span class="n">enabled</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="s2">&quot;UFW отключен. Включите UFW перед добавлением правил.&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">add_ufw_out_rules</span><span class="p">(</span><span class="n">rules</span><span class="p">)</span>
<span class="k">if</span> <span class="n">success</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span> <span class="s2">&quot;Исходящие правила UFW успешно добавлены&quot;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span>
<span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span>
<span class="n">detail</span><span class="o">=</span><span class="s2">&quot;Не удалось добавить исходящие правила UFW&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
<div class="viewcode-block" id="delete_ufw_rules">
<a class="viewcode-back" href="../../../api/endpoints/ufw.html#api.v1.ufw.delete_ufw_rules">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">&quot;/rules&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">delete_ufw_rules</span><span class="p">(</span><span class="n">rule_numbers</span><span class="p">:</span> <span class="n">RuleNumbers</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Удаляет правила UFW по их номерам.</span>
<span class="sd"> Args:</span>
<span class="sd"> rule_numbers (RuleNumbers): Номера правил, подлежащих удалению (как в</span>
<span class="sd"> выводе `ufw status numbered`).</span>
<span class="sd"> Returns:</span>
<span class="sd"> dict: Информация об общем количестве удаляемых правил, числе успешных и</span>
<span class="sd"> возможной ошибке.</span>
<span class="sd"> Raises:</span>
<span class="sd"> HTTPException: При ошибках удаления (status_code=500).</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">ufw_service</span> <span class="o">=</span> <span class="n">UFWService</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">ufw_service</span><span class="o">.</span><span class="n">delete_rules</span><span class="p">(</span><span class="n">rule_numbers</span><span class="o">.</span><span class="n">numbers</span><span class="p">)</span>
<span class="k">if</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;success&quot;</span><span class="p">]:</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span><span class="p">,</span>
<span class="s2">&quot;message&quot;</span><span class="p">:</span>
<span class="sa">f</span><span class="s2">&quot;Удалено </span><span class="si">{</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;successful&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2"> из </span><span class="si">{</span><span class="n">result</span><span class="p">[</span><span class="s1">&#39;total&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2"> правил&quot;</span><span class="p">,</span>
<span class="s2">&quot;details&quot;</span><span class="p">:</span> <span class="n">result</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="n">result</span><span class="p">[</span><span class="s2">&quot;error&quot;</span><span class="p">])</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;Error: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

View file

@ -1,488 +0,0 @@
<!doctype html>
<html class="no-js" lang="en" data-content_root="../../../">
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="../../../genindex.html"><link rel="search" title="Search" href="../../../search.html">
<!-- Generated with Sphinx 8.2.3 and Furo 2025.09.25 -->
<title>api.v1.websocket - GONETS SRV 0.0.3 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=d111a655" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo.css?v=580074bf" />
<link rel="stylesheet" type="text/css" href="../../../_static/styles/furo-extensions.css?v=8dab3a3b" />
<style>
body {
--color-code-background: #f2f2f2;
--color-code-foreground: #1e1e1e;
}
@media not print {
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024">
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 0 0 0 13.8z"/>
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" viewBox="0 0 24 24">
<title>Auto light/dark, in light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path style="opacity: 50%" d="M 5.411 14.504 C 5.471 14.504 5.532 14.504 5.591 14.504 C 3.639 16.319 4.383 19.569 6.931 20.352 C 7.693 20.586 8.512 20.551 9.25 20.252 C 8.023 23.207 4.056 23.725 2.11 21.184 C 0.166 18.642 1.702 14.949 4.874 14.536 C 5.051 14.512 5.231 14.5 5.411 14.5 L 5.411 14.504 Z"/>
<line x1="14.5" y1="3.25" x2="14.5" y2="1.25"/>
<line x1="14.5" y1="15.85" x2="14.5" y2="17.85"/>
<line x1="10.044" y1="5.094" x2="8.63" y2="3.68"/>
<line x1="19" y1="14.05" x2="20.414" y2="15.464"/>
<line x1="8.2" y1="9.55" x2="6.2" y2="9.55"/>
<line x1="20.8" y1="9.55" x2="22.8" y2="9.55"/>
<line x1="10.044" y1="14.006" x2="8.63" y2="15.42"/>
<line x1="19" y1="5.05" x2="20.414" y2="3.636"/>
<circle cx="14.5" cy="9.55" r="3.6"/>
</svg>
</symbol>
<symbol id="svg-moon-with-sun" viewBox="0 0 24 24">
<title>Auto light/dark, in dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
class="icon-custom-derived-from-feather-sun-and-tabler-moon">
<path d="M 8.282 7.007 C 8.385 7.007 8.494 7.007 8.595 7.007 C 5.18 10.184 6.481 15.869 10.942 17.24 C 12.275 17.648 13.706 17.589 15 17.066 C 12.851 22.236 5.91 23.143 2.505 18.696 C -0.897 14.249 1.791 7.786 7.342 7.063 C 7.652 7.021 7.965 7 8.282 7 L 8.282 7.007 Z"/>
<line style="opacity: 50%" x1="18" y1="3.705" x2="18" y2="2.5"/>
<line style="opacity: 50%" x1="18" y1="11.295" x2="18" y2="12.5"/>
<line style="opacity: 50%" x1="15.316" y1="4.816" x2="14.464" y2="3.964"/>
<line style="opacity: 50%" x1="20.711" y1="10.212" x2="21.563" y2="11.063"/>
<line style="opacity: 50%" x1="14.205" y1="7.5" x2="13.001" y2="7.5"/>
<line style="opacity: 50%" x1="21.795" y1="7.5" x2="23" y2="7.5"/>
<line style="opacity: 50%" x1="15.316" y1="10.184" x2="14.464" y2="11.036"/>
<line style="opacity: 50%" x1="20.711" y1="4.789" x2="21.563" y2="3.937"/>
<circle style="opacity: 50%" cx="18" cy="7.5" r="2.169"/>
</svg>
</symbol>
<symbol id="svg-pencil" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" viewBox="0 0 24 24">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-eye-code">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path
d="M11.11 17.958c-3.209 -.307 -5.91 -2.293 -8.11 -5.958c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6c-.21 .352 -.427 .688 -.647 1.008" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation" aria-label="Toggle site navigation sidebar">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc" aria-label="Toggle table of contents sidebar">
<label class="overlay sidebar-overlay" for="__navigation"></label>
<label class="overlay toc-overlay" for="__toc"></label>
<a class="skip-to-content muted-link" href="#furo-main-content">Skip to content</a>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<span class="icon"><svg><use href="#svg-menu"></use></svg></span>
</label>
</div>
<div class="header-center">
<a href="../../../index.html"><div class="brand">GONETS SRV 0.0.3 documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../../../index.html">
<span class="sidebar-brand-text">GONETS SRV 0.0.3 documentation</span>
</a><form class="sidebar-search-container" method="get" action="../../../search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">Разделы</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/services.html">Сервисы</a><input aria-label="Toggle navigation of Сервисы" class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/arp_service.html">ARP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/auth_service.html">Auth Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/connect_service.html">Connect Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/firewalld_service.html">Firewalld Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ip_service.html">IP Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/network_service.html">Network Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/settings_service.html">Settings Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/subnet_service.html">Subnet Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/ufw_service.html">UFW Service</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/services/wifi_service.html">Wifi Service</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/models.html">Модели</a><input aria-label="Toggle navigation of Модели" class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/auth.html">models.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/network.html">models.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/request.html">models.request</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/subnet.html">models.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/ufw.html">models.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/models/wifi.html">models.wifi</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/endpoints.html">API эндпоинты</a><input aria-label="Toggle navigation of API эндпоинты" class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/auth.html">api.v1.auth</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ip.html">api.v1.ip</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/network.html">api.v1.network</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/settings.html">api.v1.settings</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/subnet.html">api.v1.subnet</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/ufw.html">api.v1.ufw</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/endpoints/websocket.html">api.v1.websocket</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/config.html">Конфигурация</a><input aria-label="Toggle navigation of Конфигурация" class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/cors.html">config.cors</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/init_config.html">config.init_config</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/config/ports.html">config.ports</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/core.html">Core</a><input aria-label="Toggle navigation of Core" class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/core/cli_executor.html">core.cli_executor</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/storage.html">Хранилища</a><input aria-label="Toggle navigation of Хранилища" class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/json_storage.html">storage.json_storage</a></li>
<li class="toctree-l2"><a class="reference internal" href="../../../api/storage/session_storage.html">storage.session_storage</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../../../api/utils.html">Утилиты</a><input aria-label="Toggle navigation of Утилиты" class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><span class="icon"><svg><use href="#svg-arrow-right"></use></svg></span></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../../../api/utils/i18n.html">utils.i18n</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<div class="article-container">
<a href="#" class="back-to-top muted-link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
</svg>
<span>Back to top</span>
</a>
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle" aria-label="Toggle Light / Dark / Auto color theme">
<svg class="theme-icon-when-auto-light"><use href="#svg-sun-with-moon"></use></svg>
<svg class="theme-icon-when-auto-dark"><use href="#svg-moon-with-sun"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon no-toc" for="__toc">
<span class="icon"><svg><use href="#svg-toc"></use></svg></span>
</label>
</div>
<article role="main" id="furo-main-content">
<h1>Source code for api.v1.websocket</h1><div class="highlight"><pre>
<span></span><span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">typing</span><span class="w"> </span><span class="kn">import</span> <span class="n">Set</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">fastapi</span><span class="w"> </span><span class="kn">import</span> <span class="n">APIRouter</span><span class="p">,</span> <span class="n">WebSocket</span><span class="p">,</span> <span class="n">WebSocketDisconnect</span><span class="p">,</span> <span class="n">Depends</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">starlette.websockets</span><span class="w"> </span><span class="kn">import</span> <span class="n">WebSocketState</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">utils.i18n</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_translations</span><span class="p">,</span> <span class="n">_</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">babel.support</span><span class="w"> </span><span class="kn">import</span> <span class="n">Translations</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">.network</span><span class="w"> </span><span class="kn">import</span> <span class="n">update_container_position</span><span class="p">,</span> <span class="n">get_network_data</span>
<span class="n">router</span> <span class="o">=</span> <span class="n">APIRouter</span><span class="p">()</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<div class="viewcode-block" id="WebSocketManager">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.WebSocketManager">[docs]</a>
<span class="k">class</span><span class="w"> </span><span class="nc">WebSocketManager</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Класс для управления подключениями к WebSocket-серверу.</span>
<span class="sd"> Обрабатывает подключение, отключение и трансляцию сообщений для клиентов,</span>
<span class="sd"> подключённых через WebSocket.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Инициализирует менеджер подключений к WebSocket-серверу.</span>
<span class="sd"> Создаёт пустое множество для хранения активных соединений.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">active_connections</span><span class="p">:</span> <span class="n">Set</span><span class="p">[</span><span class="n">WebSocket</span><span class="p">]</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
<div class="viewcode-block" id="WebSocketManager.connect">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.WebSocketManager.connect">[docs]</a>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">websocket</span><span class="p">:</span> <span class="n">WebSocket</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Подключает клиента к WebSocket-серверу.</span>
<span class="sd"> Принимает подключение и добавляет WebSocket в список активных соединений.</span>
<span class="sd"> Args:</span>
<span class="sd"> websocket (WebSocket): Объект WebSocket для подключения.</span>
<span class="sd"> Raises:</span>
<span class="sd"> Exception: Если произошла ошибка при принятии подключения.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">await</span> <span class="n">websocket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">active_connections</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">websocket</span><span class="p">)</span></div>
<div class="viewcode-block" id="WebSocketManager.disconnect">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.WebSocketManager.disconnect">[docs]</a>
<span class="k">def</span><span class="w"> </span><span class="nf">disconnect</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">websocket</span><span class="p">:</span> <span class="n">WebSocket</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Отключает клиента от WebSocket-серверу.</span>
<span class="sd"> Удаляет WebSocket из списка активных соединений.</span>
<span class="sd"> Args:</span>
<span class="sd"> websocket (WebSocket): Объект WebSocket для отключения.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">active_connections</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">websocket</span><span class="p">)</span></div>
<div class="viewcode-block" id="WebSocketManager.broadcast">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.WebSocketManager.broadcast">[docs]</a>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">broadcast</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">message</span><span class="p">:</span> <span class="nb">dict</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Транслирует сообщения всем подключённым клиентам.</span>
<span class="sd"> Отправляет JSON-данные всем активным соединениям, обрабатывая</span>
<span class="sd"> отключения клиентов.</span>
<span class="sd"> Args:</span>
<span class="sd"> message (dict): Сообщение для трансляции в формате JSON.</span>
<span class="sd"> Raises:</span>
<span class="sd"> WebSocketDisconnect: Если клиент неожиданно отключился.</span>
<span class="sd"> Exception: Если произошла другая ошибка при отправке.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">connection</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">active_connections</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="n">connection</span><span class="o">.</span><span class="n">client_state</span> <span class="o">==</span> <span class="n">WebSocketState</span><span class="o">.</span><span class="n">CONNECTED</span><span class="p">:</span>
<span class="k">await</span> <span class="n">connection</span><span class="o">.</span><span class="n">send_json</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
<span class="k">except</span> <span class="n">WebSocketDisconnect</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">connection</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Broadcast error: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">connection</span><span class="p">)</span></div>
</div>
<span class="n">manager</span> <span class="o">=</span> <span class="n">WebSocketManager</span><span class="p">()</span>
<div class="viewcode-block" id="websocket_endpoint">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.websocket_endpoint">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">websocket</span><span class="p">(</span><span class="s2">&quot;/api/v1&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">websocket_endpoint</span><span class="p">(</span><span class="n">websocket</span><span class="p">:</span> <span class="n">WebSocket</span><span class="p">,</span> <span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Обработчик WebSocket-соединения.</span>
<span class="sd"> Управляет соединением клиента, принимает данные для обновления позиций</span>
<span class="sd"> контейнеров и транслирует сетевые данные.</span>
<span class="sd"> Args:</span>
<span class="sd"> websocket (WebSocket): Объект WebSocket для обработки соединения.</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Raises:</span>
<span class="sd"> WebSocketDisconnect: Если клиент отключился.</span>
<span class="sd"> Exception: Если произошла ошибка при обработке данных.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">await</span> <span class="n">manager</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">websocket</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">websocket</span><span class="o">.</span><span class="n">receive_json</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Received data: </span><span class="si">{</span><span class="n">data</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">all</span><span class="p">(</span><span class="n">key</span> <span class="ow">in</span> <span class="n">data</span> <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">&quot;container_id&quot;</span><span class="p">,</span> <span class="s2">&quot;container_x&quot;</span><span class="p">,</span> <span class="s2">&quot;container_y&quot;</span><span class="p">]):</span>
<span class="k">await</span> <span class="n">update_container_position</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">network_data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">get_network_data</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Current network data: </span><span class="si">{</span><span class="n">network_data</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">await</span> <span class="n">manager</span><span class="o">.</span><span class="n">broadcast</span><span class="p">(</span><span class="n">network_data</span><span class="p">)</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="k">except</span> <span class="n">WebSocketDisconnect</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Client disconnected&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in websocket_endpoint: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">manager</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">websocket</span><span class="p">)</span>
<span class="k">if</span> <span class="n">websocket</span><span class="o">.</span><span class="n">client_state</span> <span class="o">!=</span> <span class="n">WebSocketState</span><span class="o">.</span><span class="n">DISCONNECTED</span><span class="p">:</span>
<span class="k">await</span> <span class="n">websocket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span></div>
<div class="viewcode-block" id="startup_event">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.startup_event">[docs]</a>
<span class="nd">@router</span><span class="o">.</span><span class="n">on_event</span><span class="p">(</span><span class="s2">&quot;startup&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">startup_event</span><span class="p">():</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Запуск таска для трансляции сетевых данных при старте приложения.</span>
<span class="sd"> Создаёт асинхронную задачу для периодической трансляции данных.</span>
<span class="sd"> Raises:</span>
<span class="sd"> Exception: Если произошла ошибка при запуске таска.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">broadcast_data</span><span class="p">())</span></div>
<div class="viewcode-block" id="broadcast_data">
<a class="viewcode-back" href="../../../api/endpoints/websocket.html#api.v1.websocket.broadcast_data">[docs]</a>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">broadcast_data</span><span class="p">(</span><span class="n">translations</span><span class="p">:</span> <span class="n">Translations</span> <span class="o">=</span> <span class="n">Depends</span><span class="p">(</span><span class="n">get_translations</span><span class="p">())):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Таск для периодической трансляции сетевых данных.</span>
<span class="sd"> Выполняет сканирование сети и отправляет данные всем подключённым клиентам</span>
<span class="sd"> с интервалом в 2 секунды.</span>
<span class="sd"> Args:</span>
<span class="sd"> translations: Объект переводов для текущего языка.</span>
<span class="sd"> Raises:</span>
<span class="sd"> Exception: Если произошла ошибка при получении данных или трансляции.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">network_data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">get_network_data</span><span class="p">()</span>
<span class="k">if</span> <span class="s2">&quot;activation&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">network_data</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="n">translations</span><span class="o">.</span><span class="n">gettext</span><span class="p">(</span><span class="s2">&quot;Ошибка: Ключ &#39;activation&#39; отсутствует в полученных данных сети&quot;</span><span class="p">))</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="k">continue</span>
<span class="k">await</span> <span class="n">manager</span><span class="o">.</span><span class="n">broadcast</span><span class="p">(</span><span class="n">network_data</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Error in broadcast_data: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span></div>
</pre></div>
</article>
</div>
<footer>
<div class="related-pages">
</div>
<div class="bottom-of-page">
<div class="left-details">
<div class="copyright">
Copyright &#169; 2025, GONETS
</div>
Made with <a href="https://www.sphinx-doc.org/">Sphinx</a> and <a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo</a>
</div>
<div class="right-details">
</div>
</div>
</footer>
</div>
<aside class="toc-drawer no-toc">
</aside>
</div>
</div><script src="../../../_static/documentation_options.js?v=47de8214"></script>
<script src="../../../_static/doctools.js?v=9bcbadda"></script>
<script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../../_static/scripts/furo.js?v=46bd48cc"></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show more