From 4257864b3d49ab168cd9169fe3d8eb3fe4d0360f Mon Sep 17 00:00:00 2001 From: "s.galanskiy" Date: Fri, 4 Jul 2025 13:48:38 +0500 Subject: [PATCH 01/50] =?UTF-8?q?=D0=9D=D0=B0=D1=82=D0=B8=D0=B2=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B5=D1=82=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=81=D0=BA=D0=B0=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D1=83=D0=BC=D0=BE=D0=BB?= =?UTF-8?q?=D1=87=D0=B0=D0=BD=D0=B8=D1=8E=20(192.168.1.0/24).=20=D0=9E?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA=20=D0=BF=D1=80=D0=B8=20=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D1=81=D1=82=D0=BA=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SRV/api/v1/subnet.py | 12 ++++++------ SRV/models/subnet.py | 16 ++++++++-------- SRV/services/settings_service.py | 20 ++++++++++++++++---- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/SRV/api/v1/subnet.py b/SRV/api/v1/subnet.py index 43e4d1c..f08b0b4 100644 --- a/SRV/api/v1/subnet.py +++ b/SRV/api/v1/subnet.py @@ -1,6 +1,5 @@ -from fastapi import APIRouter, HTTPException -from typing import List, Dict -from models.subnet import SubnetScanResult +from fastapi import APIRouter, HTTPException, Body +from models.subnet import SubnetScanResult, SubnetScanRequest from models.wifi import WifiScanResult from models.request import NoteRequest, WifiConnectRequest, WifiDisconnectRequest from services.subnet_service import SubnetService @@ -9,8 +8,8 @@ from services.connect_service import WifiConnectionService router = APIRouter() -@router.get("/devices", response_model=SubnetScanResult) -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(...)): """Сканирует локальную подсеть и возвращает данные об устройствах. Выполняет сканирование указанной подсети для обнаружения активных устройств @@ -29,7 +28,8 @@ def scan_subnet(subnet: str = "192.168.1.0/24", ports: List[int] = None): """ try: subnet_service = SubnetService() - result = subnet_service.scan_subnet(subnet, ports) + subnet = request.subnet or "192.168.1.0/24" + result = subnet_service.scan_subnet(subnet) return result except Exception as e: raise HTTPException(status_code=500, detail=f"Ошибка сканирования: {str(e)}") diff --git a/SRV/models/subnet.py b/SRV/models/subnet.py index d281cfe..f46c9b3 100644 --- a/SRV/models/subnet.py +++ b/SRV/models/subnet.py @@ -37,11 +37,11 @@ class SubnetScanResult(BaseModel): devices: List[Device] = [] -#class SubnetScanRequest(BaseModel): -# """ -# Параметры для сканирования подсети -# -# Атрибуты: -# subnet (str): Подсеть для сканирования (например 192.168.1.0/24) -# """ -# subnet: Optional[str] = "192.168.1.0/24" +class SubnetScanRequest(BaseModel): + """ + Параметры для сканирования подсети + + Атрибуты: + subnet (str): Подсеть для сканирования (например 192.168.1.0/24) + """ + subnet: Optional[str] = "192.168.1.0/24" diff --git a/SRV/services/settings_service.py b/SRV/services/settings_service.py index 4b6d9cc..5399b96 100644 --- a/SRV/services/settings_service.py +++ b/SRV/services/settings_service.py @@ -25,6 +25,9 @@ class SettingsService: json.dump([], f) logger.info("Данные узлов подсети успешно очищены") return True + except FileNotFoundError: + logger.info("Файл устройств не найден, очистка считается успешной") + return True except Exception as e: logger.error(f"Ошибка очистки узлов подсети: {str(e)}") raise @@ -43,6 +46,9 @@ class SettingsService: json.dump([], f) logger.info("Данные точек доступа Wi-Fi успешно очищены") return True + except FileNotFoundError: + logger.info("Файл Wi-Fi не найден, очистка считается успешной") + return True except Exception as e: logger.error(f"Ошибка очистки точек доступа Wi-Fi: {str(e)}") raise @@ -57,10 +63,16 @@ class SettingsService: Exception: Если произошла ошибка при записи в один из файлов. """ try: - with open(self.subnet_file, "w") as f: - json.dump([], f) - with open(self.wifi_file, "w") as f: - json.dump([], f) + try: + with open(self.subnet_file, "w") as f: + json.dump([], f) + except FileNotFoundError: + logger.info("Файл устройств не найден, очистка считается успешной") + try: + with open(self.wifi_file, "w") as f: + json.dump([], f) + except FileNotFoundError: + logger.info("Файл Wi-Fi не найден, очистка считается успешной") logger.info("Все сетевые данные успешно очищены") return True except Exception as e: From ee350c1852d60281de7466cd426bb97a30adc45c Mon Sep 17 00:00:00 2001 From: test Date: Fri, 4 Jul 2025 15:07:41 +0500 Subject: [PATCH 02/50] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=20=D0=B8=20=D0=B8=D1=85=20=D1=84=D1=83=D0=BD=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/workModules/networkInterfaces.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/src/api/request.ts | 18 ++- .../modalContainers/customizeNodes.tsx | 131 ++++++++++++++++++ .../components/modalwindows/modalWindow.tsx | 4 + .../DEV/src/components/modalwindows/style.ts | 1 + .../components/modalwindows/windowsType.ts | 2 + .../src/components/settings/SettingItem.tsx | 18 ++- .../DEV/src/components/settings/Settings.tsx | 11 +- .../usersScanner/modules/TableScanner.tsx | 95 +++++++++---- .../appModules/usersScanner/userScanner.tsx | 2 +- .../appModules/wifiScanner/WifiScanner.tsx | 2 +- .../modules/workModules/networkInterfaces.tsx | 2 +- .../workSpace/sideBar/toolBar/toolBar.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/src/electron.d.ts | 4 + FRONT/DEV/src/hooks/useSettings.ts | 14 ++ FRONT/DEV/src/store/ipStore.ts | 2 +- .../store/settings/config/networkSettings.ts | 22 ++- .../store/settings/config/nodesSettings.ts | 79 +++++++++-- FRONT/DEV/src/store/settings/settingsStore.ts | 7 +- FRONT/DEV/src/store/themeContext.tsx | 33 +---- SRV/main.py | 4 +- 23 files changed, 368 insertions(+), 91 deletions(-) create mode 100644 FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { return new Promise((resolve) => { - const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`); + const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const timeout = setTimeout(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index 11f08de..ce00ee9 100644 --- a/FRONT/DEV/src/api/request.ts +++ b/FRONT/DEV/src/api/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosResponse } from "axios"; const axiosWithoutCredentials = axios.create({ - baseURL: `https://localhost:8001`, + baseURL: `http://localhost:8001`, }) export class NetworkService { static createAxiosInstance(ipAddress: string) { return axios.create({ - baseURL: `https://${ipAddress}:8001`, + baseURL: `http://${ipAddress}:8001`, }); } @@ -86,7 +86,9 @@ export class NetworkService { static async scanUser(ipAddress: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); - return await axiosInstance.get('/subnet/devices'); + return await axiosInstance.post('/subnet/devices',{ + subnet: '' + }); } catch (error: any) { return error.response; } @@ -157,6 +159,8 @@ export class NetworkService { return error.response; } } + + // Settings static async clearSubnet(ipAddress: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); @@ -173,4 +177,12 @@ export class NetworkService { return error.response; } } + static async exportDevices(ipAddress: string): Promise { + try { + const axiosInstance = this.createAxiosInstance(ipAddress); + return await axiosInstance.post(`/settings/export`); + } catch (error: any) { + return error.response; + } + } } \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx new file mode 100644 index 0000000..dfa8037 --- /dev/null +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx @@ -0,0 +1,131 @@ +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'; + +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 [dragIndex, setDragIndex] = useState(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]); + + return ( + + +

Кастомизация таблицы узлов подсети

+ +
+ + + + {items.map((item:string, index:number) => ( + handleDragStart(index)} + onDragOver={(e) => { + e.preventDefault(); + handleDragOver(index); + }} + onDragEnd={handleDrop} + onClick={() => handleSwitch(index)} + > + {item} + + ))} + + + + + + +
+ ); +}; + +export default CustomizeNodes; \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalWindow.tsx b/FRONT/DEV/src/components/modalwindows/modalWindow.tsx index b6dbac9..f745b4f 100644 --- a/FRONT/DEV/src/components/modalwindows/modalWindow.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalWindow.tsx @@ -8,6 +8,7 @@ import { NoteWifi } from "./modalContainers/NoteWifi"; import {ConnectNetwork} from "./modalContainers/ConnectNetwork"; import {DisconnectConfirm} from "./modalContainers/disconnectConfirm"; import {Settings} from "../settings/Settings"; +import CustomizeNodes from "./modalContainers/customizeNodes"; type Props = { isOpen: boolean, @@ -80,6 +81,9 @@ export default function WindowComponent({ isOpen, type, data }: Props) { onClose={closeLoader} /> )} + {type === ModalMap['11'] && ( + + )} } diff --git a/FRONT/DEV/src/components/modalwindows/style.ts b/FRONT/DEV/src/components/modalwindows/style.ts index 2e588c2..f0a2639 100644 --- a/FRONT/DEV/src/components/modalwindows/style.ts +++ b/FRONT/DEV/src/components/modalwindows/style.ts @@ -32,6 +32,7 @@ export const ModalTitle = styled.div` padding: 20px; border-bottom: 1px solid #9D9D9D; display: flex; + align-items: center; justify-content: space-between; h3{ diff --git a/FRONT/DEV/src/components/modalwindows/windowsType.ts b/FRONT/DEV/src/components/modalwindows/windowsType.ts index ae6d760..9193a6f 100644 --- a/FRONT/DEV/src/components/modalwindows/windowsType.ts +++ b/FRONT/DEV/src/components/modalwindows/windowsType.ts @@ -10,4 +10,6 @@ export const ModalMap: ModalType = { '7': 'noteWifi', '8': 'connectNetwork', '9': 'disconnectConfirm', + "10": 'settings', + '11': 'customNodes', } diff --git a/FRONT/DEV/src/components/settings/SettingItem.tsx b/FRONT/DEV/src/components/settings/SettingItem.tsx index f1f850c..80014d1 100644 --- a/FRONT/DEV/src/components/settings/SettingItem.tsx +++ b/FRONT/DEV/src/components/settings/SettingItem.tsx @@ -6,6 +6,8 @@ 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"; const SettingBlock = styled.div` display: flex; @@ -19,6 +21,7 @@ const SettingBlock = styled.div` 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; @@ -73,7 +76,7 @@ const InputWrapper = styled.div` gap: 6px; ` const ErrorText = styled.div` - color: red; + color: ${props => props.theme.errorColor}; font-size: 12px; `; @@ -87,7 +90,17 @@ interface SettingItemProps { const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => { const theme = useTheme(); const [error, setError] = useState(null); + const { openModal } = useModalContext(); + const handleButtonAction = () => { + if (setting.id === 'customize-nodes') { + openModal(ModalMap['11']); + } else if (setting.action) { + setting.action(); + } + }; + + // Изменение числа const handleNumberChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; let newError: string | null = null; @@ -172,9 +185,10 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: type="button" size="small" title={setting.defaultValue || ''} - onClick={setting.action} + onClick={handleButtonAction} /> )} + {setting.type === 'hidden' && null} ); }); diff --git a/FRONT/DEV/src/components/settings/Settings.tsx b/FRONT/DEV/src/components/settings/Settings.tsx index eb1aad9..9723bc4 100644 --- a/FRONT/DEV/src/components/settings/Settings.tsx +++ b/FRONT/DEV/src/components/settings/Settings.tsx @@ -6,8 +6,6 @@ import {Setting, useSettingsStore} from "../../store/settings/settingsStore"; import {SettingItem} from "./SettingItem"; import {IconComponent} from "../../const/buttons/icons"; import {buttonNames} from "../../const/buttons/names"; -import {ModalMap} from "../modalwindows/windowsType"; -import {workSpaceNames} from "../../const/workspace/names"; interface Props { onClose: () => void; @@ -129,7 +127,7 @@ export const Settings = ({ onClose }: Props) => { }, [discardChanges, onClose]); return ( - +

Настройки

@@ -168,6 +166,13 @@ export const Settings = ({ onClose }: Props) => { + ` const TableScanner = ({devices}:TableScannerProps) => { const { openModal } = useModalContext(); + const nameSetting = useNameSetting(); const openModalNote = (e: React.MouseEvent, device:DeviceUser ) => { e.stopPropagation(); @@ -75,7 +80,27 @@ const TableScanner = ({devices}:TableScannerProps) => { currentNote: device.note || '' }); }; - const nameSetting = useNameSetting(); + + // Применение настроек отображения + const ordersColumns = useOrdersColumns(); + const disabledColumns = useDisabledColumns(); + const columnVisibility = useMemo(() => { + return { + status: !disabledColumns.includes(ordersColumns.indexOf('Онлайн')), + name: !disabledColumns.includes(ordersColumns.indexOf('Имя')), + vendor: !disabledColumns.includes(ordersColumns.indexOf('Производитель')), + ip: !disabledColumns.includes(ordersColumns.indexOf('IP адрес')), + ports: !disabledColumns.includes(ordersColumns.indexOf('Открытые порты')), + activity: !disabledColumns.includes(ordersColumns.indexOf('Последняя активность')), + note: !disabledColumns.includes(ordersColumns.indexOf('Заметка')), + action: !disabledColumns.includes(ordersColumns.indexOf('Действие')) + }; + }, [ordersColumns, disabledColumns]); + const sortedColumns = useMemo(() => { + return ordersColumns.filter(column => + !disabledColumns.includes(ordersColumns.indexOf(column)) + ); + }, [ordersColumns, disabledColumns]); // Пагинация const paginateSetting = usePaginateSetting(); @@ -105,32 +130,42 @@ const TableScanner = ({devices}:TableScannerProps) => { - - - Имя - Производитель - IP адрес - Открытые порты - Последняя активность - Заметка - - + + {columnVisibility.status && } + {columnVisibility.name && Имя} + {columnVisibility.vendor && Производитель} + {columnVisibility.ip && IP адрес} + {columnVisibility.ports && Открытые порты} + {columnVisibility.activity && Последняя активность} + {columnVisibility.note && Заметка} + {columnVisibility.action && } + {currentDevices.map((item, i) => ( - - {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')} - {item.vendor} - {item.ip} - {item.open_ports.join(', ') || '-'} - {item.last_seen} - {item.note || '-'} - - - + {columnVisibility.status && ( + + )} + {columnVisibility.name && ( + + {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')} + + )} + {columnVisibility.vendor && {item.vendor}} + {columnVisibility.ip && {item.ip}} + {columnVisibility.ports && ( + {item.open_ports.join(', ') || '-'} + )} + {columnVisibility.activity && {item.last_seen}} + {columnVisibility.note && {item.note || '-'}} + {columnVisibility.action && ( + + + + )} ))} diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx index 8a098b8..dbb8821 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx @@ -144,7 +144,7 @@ export default function UserScanner() { } setScanInitiated(true); const res = await NetworkService.scanUser(ipAddress); - console.log(res); + console.log(res) setDevices(res.data.devices || []); } catch (err) { console.error("Ошибка сканирования сети:", err); diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx index 25000eb..ea4d20b 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx @@ -137,7 +137,7 @@ const ScanBtn = styled.button<{scanInitiated: boolean}>` export function WifiScanner() { //const { ipAddress } = useIpStore(); - const ipAddress = '192.168.1.147' + const ipAddress = '192.168.1.148' const [loading, setLoading] = useState(false); const [scanInitiated, setScanInitiated] = useState(false); const { wifiNetworks, setWifiNetworks } = useNetworkStore(); diff --git a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx index 1f49b9c..f6d5825 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx @@ -69,7 +69,7 @@ export const ToolBar = () => { - + {/**/} + + ); + default: + return null; + } + }; return ( - - - - - {columnVisibility.status && } - {columnVisibility.name && Имя} - {columnVisibility.vendor && Производитель} - {columnVisibility.ip && IP адрес} - {columnVisibility.ports && Открытые порты} - {columnVisibility.activity && Последняя активность} - {columnVisibility.note && Заметка} - {columnVisibility.action && } - - - - {currentDevices.map((item, i) => ( - - {columnVisibility.status && ( - - )} - {columnVisibility.name && ( - - {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')} - - )} - {columnVisibility.vendor && {item.vendor}} - {columnVisibility.ip && {item.ip}} - {columnVisibility.ports && ( - {item.open_ports.join(', ') || '-'} - )} - {columnVisibility.activity && {item.last_seen}} - {columnVisibility.note && {item.note || '-'}} - {columnVisibility.action && ( - - - - )} - - ))} - - - {totalPages > 1 && ( - - )} - - ); -}; + + + + + {visibleColumns.map(column => { + switch (column) { + case 'status': return ; + case 'name': return Имя; + case 'vendor': return Производитель; + case 'ip': return IP адрес; + case 'ports': return Открытые порты; + case 'activity': return Последняя активность; + case 'note': return Заметка; + case 'action': return ; + default: return null; + } + })} + + + + {currentDevices.map((item, index) => ( + + {visibleColumns.map(column => renderTableCell(column, item))} + + ))} + + -export default TableScanner; \ No newline at end of file + {totalPages > 1 && ( + + )} + + ); +}; \ No newline at end of file diff --git a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx index d0325ef..1996ddd 100644 --- a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index dd5ca2b..8b919f4 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { 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(() => { testSocket.close(); resolve(false); From 26a364aad6dc04d237ff4756cd322508cdffb2b1 Mon Sep 17 00:00:00 2001 From: "s.galanskiy" Date: Fri, 4 Jul 2025 15:27:40 +0500 Subject: [PATCH 05/50] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BA=D1=82=20=D0=BE?= =?UTF-8?q?=D1=88=D0=B8=D0=B1=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/appModules/usersScanner/modules/TableUsers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx index 5a3bc4f..22f9086 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import DeviceCard from "../../../../../UI/Dropdown/DeviceCard"; import {DeviceUser} from "../../../../../../store/useDeviceStore"; -import TableScanner from "./TableScanner"; +import {TableScanner} from "./TableScanner"; const ScanBtn = styled.button` position: relative; From ca33a5537532074f7a8132ea9d5406b2a279ec0a Mon Sep 17 00:00:00 2001 From: test Date: Fri, 4 Jul 2025 15:54:02 +0500 Subject: [PATCH 06/50] =?UTF-8?q?=D0=AD=D0=BB=D0=B5=D0=BA=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BD=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/workModules/networkInterfaces.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/preload.js | 22 +++---- FRONT/DEV/src/api/request.ts | 4 +- .../DEV/src/components/settings/Settings.tsx | 62 ++++++++++++++----- .../usersScanner/modules/TableScanner.tsx | 2 +- .../usersScanner/modules/TableUsers.tsx | 4 +- .../modules/workModules/networkInterfaces.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/src/store/settings/settingsStore.ts | 11 ++++ 10 files changed, 74 insertions(+), 39 deletions(-) diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { return new Promise((resolve) => { - const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`); + const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const timeout = setTimeout(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/preload.js b/FRONT/DEV/preload.js index 3e21ba6..1ceb420 100644 --- a/FRONT/DEV/preload.js +++ b/FRONT/DEV/preload.js @@ -1,19 +1,11 @@ const { contextBridge, ipcRenderer } = require('electron'); -contextBridge.exposeInMainWorld('electron', { - send: (channel, data) => { - ipcRenderer.send(channel, data); - }, - on: (channel, func) => { - ipcRenderer.on(channel, (event, ...args) => func(...args)); - }, - quitApp: () => { - ipcRenderer.send('quit-app'); - }, +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'), - onThemeChange: (callback) => { - ipcRenderer.on('theme-update', (event, theme) => { - callback(theme); - }); - } + 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)) }); \ No newline at end of file diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index d0ad0b1..ce00ee9 100644 --- a/FRONT/DEV/src/api/request.ts +++ b/FRONT/DEV/src/api/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosResponse } from "axios"; const axiosWithoutCredentials = axios.create({ - baseURL: `https://localhost:8001`, + baseURL: `http://localhost:8001`, }) export class NetworkService { static createAxiosInstance(ipAddress: string) { return axios.create({ - baseURL: `https://${ipAddress}:8001`, + baseURL: `http://${ipAddress}:8001`, }); } diff --git a/FRONT/DEV/src/components/settings/Settings.tsx b/FRONT/DEV/src/components/settings/Settings.tsx index 9723bc4..a086a0d 100644 --- a/FRONT/DEV/src/components/settings/Settings.tsx +++ b/FRONT/DEV/src/components/settings/Settings.tsx @@ -1,12 +1,13 @@ import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../modalwindows/style"; import { MainButton } from "../UI/buttons/mainButton"; import {useCallback, useEffect, useMemo, useState} from "react"; -import styled from "styled-components"; +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"; + interface Props { onClose: () => void; } @@ -47,6 +48,16 @@ const SectionLine = styled.span` 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); @@ -57,10 +68,13 @@ export const Settings = ({ onClose }: Props) => { 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 theme = useTheme() const [fieldErrors, setFieldErrors] = useState>({}); + // Проверка действий const handleValidationError = useCallback((fieldId: string, error: string | null) => { setFieldErrors(prev => { const newErrors = { ...prev }; @@ -126,6 +140,12 @@ export const Settings = ({ onClose }: Props) => { onClose(); }, [discardChanges, onClose]); + const handleReset = () => { + if (window.confirm('Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?')) { + resetToDefaults(); + } + }; + return ( @@ -166,20 +186,32 @@ export const Settings = ({ onClose }: Props) => { - - + + + + + + + ); diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx index d317634..0817650 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx @@ -107,7 +107,7 @@ export const TableScanner = ({ devices }: TableScannerProps) => { default: return ''; } }) - .filter(Boolean); // Убираем пустые значения + .filter(Boolean); }, [ordersColumns, disabledColumns]); // Пагинация diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx index 5a3bc4f..230034f 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx @@ -1,8 +1,8 @@ import React from 'react'; import styled from 'styled-components'; -import DeviceCard from "../../../../../UI/Dropdown/DeviceCard"; import {DeviceUser} from "../../../../../../store/useDeviceStore"; -import TableScanner from "./TableScanner"; +import {TableScanner} from "./TableScanner"; + const ScanBtn = styled.button` position: relative; diff --git a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { return new Promise((resolve) => { - const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`); + const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const timeout = setTimeout(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/src/store/settings/settingsStore.ts b/FRONT/DEV/src/store/settings/settingsStore.ts index 45c8440..b26c98d 100644 --- a/FRONT/DEV/src/store/settings/settingsStore.ts +++ b/FRONT/DEV/src/store/settings/settingsStore.ts @@ -33,6 +33,7 @@ type SettingsState = { resetSettings: () => void; applyChanges: () => void; discardChanges: () => void; + resetToDefaults: () => void; getSettingValue: (id: string) => T; getNumberSetting: (id: string, fallback?: number) => number; }; @@ -58,6 +59,16 @@ export const useSettingsStore = create()( 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 }, From e896a13f3c7346d66d197f7d0651da619e25a764 Mon Sep 17 00:00:00 2001 From: test Date: Fri, 4 Jul 2025 16:01:31 +0500 Subject: [PATCH 07/50] Delete Head --- .../workSpace/modules/workModules/networkInterfaces.tsx | 2 +- .../src/components/workSpace/sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/src/api/request.ts | 4 ++-- .../modules/appModules/usersScanner/modules/TableUsers.tsx | 4 ---- .../workSpace/modules/workModules/networkInterfaces.tsx | 2 +- .../src/components/workSpace/sideBar/toolSpace/devicesBar.tsx | 2 +- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx index d0325ef..1996ddd 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index dd5ca2b..8b919f4 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { 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(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index ce00ee9..d0ad0b1 100644 --- a/FRONT/DEV/src/api/request.ts +++ b/FRONT/DEV/src/api/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosResponse } from "axios"; const axiosWithoutCredentials = axios.create({ - baseURL: `http://localhost:8001`, + baseURL: `https://localhost:8001`, }) export class NetworkService { static createAxiosInstance(ipAddress: string) { return axios.create({ - baseURL: `http://${ipAddress}:8001`, + baseURL: `https://${ipAddress}:8001`, }); } diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx index 9d15ff9..771292e 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx @@ -2,10 +2,6 @@ import React from 'react'; import styled from 'styled-components'; import {DeviceUser} from "../../../../../../store/useDeviceStore"; import {TableScanner} from "./TableScanner"; -<<<<<<< HEAD - -======= ->>>>>>> 26a364aad6dc04d237ff4756cd322508cdffb2b1 const ScanBtn = styled.button` position: relative; diff --git a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx index d0325ef..1996ddd 100644 --- a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index dd5ca2b..8b919f4 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { 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(() => { testSocket.close(); resolve(false); From 486d46c46733f437a15e92bcf2e1fd21d702c6d6 Mon Sep 17 00:00:00 2001 From: "s.galanskiy" Date: Fri, 4 Jul 2025 16:03:59 +0500 Subject: [PATCH 08/50] =?UTF-8?q?=D0=AD=D0=BA=D1=81=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=20=D1=82=D0=BE=D1=87=D0=B5=D0=BA=20=D0=B4=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=83=D0=BF=D0=B0=20=D0=B8=20=D1=83=D0=B7=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B5=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SRV/api/v1/settings.py | 41 +++++++++++++++++++++++++++++++++++++---- SRV/main.py | 4 ++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/SRV/api/v1/settings.py b/SRV/api/v1/settings.py index 4331550..bd491e8 100644 --- a/SRV/api/v1/settings.py +++ b/SRV/api/v1/settings.py @@ -72,14 +72,47 @@ def clear_all_data(): except Exception as e: raise HTTPException(status_code=500, detail=f"Ошибка очистки: {str(e)}") -DEVICE_PATH = "/home/merelden/main/projects/all/gonets/SRV/storage/devices/devices.json" +DEVICE_PATH = "storage/devices/devices.json" +WIFI_PATH = "storage/wifi/wifi.json" -@router.get("/export", response_class=FileResponse) +@router.get("/export_devices", response_class=FileResponse) async def export_devices(): + """ + Экспортирует файл с данными устройств в формате JSON. + + Returns: + FileResponse: Файл devices.json для скачивания. + + Raises: + HTTPException: Если файл не найден (status_code=404). + """ if not os.path.exists(DEVICE_PATH): - return {"error": "Файл не найден"} + logger.warning(f"Файл для экспорта не найден: {DEVICE_PATH}") + raise HTTPException(status_code=404, detail="Файл не найден") + logger.info(f"Экспорт файла: {DEVICE_PATH}") return FileResponse( path=DEVICE_PATH, - filename="device.json", + filename="devices.json", + media_type="application/json" + ) + +@router.get("/export_wifi", response_class=FileResponse) +async def export_wifi(): + """ + Экспортирует файл с данными устройств в формате JSON. + + Returns: + FileResponse: Файл devices.json для скачивания. + + Raises: + HTTPException: Если файл не найден (status_code=404). + """ + if not os.path.exists(WIFI_PATH): + logger.warning(f"Файл для экспорта не найден: {WIFI_PATH}") + raise HTTPException(status_code=404, detail="Файл не найден") + logger.info(f"Экспорт файла: {WIFI_PATH}") + return FileResponse( + path=WIFI_PATH, + filename="wifi.json", media_type="application/json" ) \ No newline at end of file diff --git a/SRV/main.py b/SRV/main.py index 1f01b6b..3511099 100644 --- a/SRV/main.py +++ b/SRV/main.py @@ -18,5 +18,5 @@ app.include_router(router) network_service = NetworkService() if __name__ == "__main__": - #uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt") - uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True) + uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt") + #uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True) From 48e8551b0eb4b9c4c6229f1d963f464351a947df Mon Sep 17 00:00:00 2001 From: test Date: Mon, 7 Jul 2025 17:35:46 +0500 Subject: [PATCH 09/50] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=BC=D0=B5=D0=BB=D0=BE=D1=87=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B8=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=BE=20=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=BD=D0=B0=D1=86.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/workModules/networkInterfaces.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/index.tsx | 57 +++++++------ FRONT/DEV/public/locales/en/common.json | 22 +++++ FRONT/DEV/public/locales/ru/common.json | 22 +++++ FRONT/DEV/src/api/request.ts | 18 ++-- .../DEV/src/components/settings/Settings.tsx | 14 +++ .../usersScanner/modules/IpScanning.tsx | 85 +++++++++++++++++++ .../usersScanner/modules/ListUsers.tsx | 17 ++-- .../usersScanner/modules/TableScanner.tsx | 34 ++++---- .../usersScanner/modules/TableUsers.tsx | 10 ++- .../appModules/usersScanner/userScanner.tsx | 61 ++++++++++--- .../appModules/wifiScanner/WifiScanner.tsx | 1 + .../modules/workModules/networkInterfaces.tsx | 2 +- .../sideBar/toolSpace/devicesBar.tsx | 2 +- FRONT/DEV/src/hooks/useSettings.ts | 4 +- .../store/settings/config/networkSettings.ts | 21 +++++ FRONT/DEV/src/utils/i18n/config.ts | 11 +++ FRONT/DEV/src/utils/i18n/hooks.ts | 11 +++ FRONT/DEV/src/utils/i18n/provider.tsx | 84 ++++++++++++++++++ SRV/main.py | 4 +- 21 files changed, 408 insertions(+), 76 deletions(-) create mode 100644 FRONT/DEV/public/locales/en/common.json create mode 100644 FRONT/DEV/public/locales/ru/common.json create mode 100644 FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/IpScanning.tsx create mode 100644 FRONT/DEV/src/utils/i18n/config.ts create mode 100644 FRONT/DEV/src/utils/i18n/hooks.ts create mode 100644 FRONT/DEV/src/utils/i18n/provider.tsx diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { return new Promise((resolve) => { - const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`); + const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const timeout = setTimeout(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/index.tsx b/FRONT/DEV/index.tsx index eeb0635..eb6f456 100644 --- a/FRONT/DEV/index.tsx +++ b/FRONT/DEV/index.tsx @@ -3,7 +3,7 @@ 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.ts' +import { useCheckAdmin } from './src/utils/checkAdmin'; import { ModalProvider, @@ -11,28 +11,44 @@ import { ThemeProvider } from './src/store'; -import WindowComponent from './src/components/modalwindows/modalWindow.tsx'; -import Router from './src/router.tsx'; +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 ( + + + + + + + + + + ); +}; const MainComponent = () => { - - const checkAdmin = useCheckAdmin() - const {ipAddress} = useIpStore() + const checkAdmin = useCheckAdmin(); + const { ipAddress } = useIpStore(); useEffect(() => { const checkAdminStatus = async () => { - await checkAdmin() - } - checkAdminStatus() - }, [ipAddress]) - + await checkAdmin(); + }; + checkAdminStatus(); + }, [ipAddress]); const { isOpen, modalType, modalData } = useModalContext(); return ( - <> - - - + <> + + + ); }; @@ -40,13 +56,6 @@ const container = document.getElementById('root'); if (!container) { throw new Error('Root element with id "root" not found'); } + const root = createRoot(container); -root.render( - - - - - - - -); \ No newline at end of file +root.render(); \ No newline at end of file diff --git a/FRONT/DEV/public/locales/en/common.json b/FRONT/DEV/public/locales/en/common.json new file mode 100644 index 0000000..7d98f05 --- /dev/null +++ b/FRONT/DEV/public/locales/en/common.json @@ -0,0 +1,22 @@ +{ + "userScanner-title": "Subnet nodes scanning", + "userScanner-visible": "Display mode", + "userScanner-loading": "Scanning nodes", + "userScanner-refresh": "Refresh list", + "userScanner-scan": "Scan nodes", + "userScanner-notFound": "No nodes found", + "tableScanner-online": "Online", + "tableScanner-name": "Name", + "tableScanner-vendor": "Manufacturer", + "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": "Please enter a valid IP address" +} \ No newline at end of file diff --git a/FRONT/DEV/public/locales/ru/common.json b/FRONT/DEV/public/locales/ru/common.json new file mode 100644 index 0000000..f4a33eb --- /dev/null +++ b/FRONT/DEV/public/locales/ru/common.json @@ -0,0 +1,22 @@ +{ + "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-адрес" +} diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index d0ad0b1..822930d 100644 --- a/FRONT/DEV/src/api/request.ts +++ b/FRONT/DEV/src/api/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosResponse } from "axios"; const axiosWithoutCredentials = axios.create({ - baseURL: `https://localhost:8001`, + baseURL: `http://localhost:8001`, }) export class NetworkService { static createAxiosInstance(ipAddress: string) { return axios.create({ - baseURL: `https://${ipAddress}:8001`, + baseURL: `http://${ipAddress}:8001`, }); } @@ -83,11 +83,11 @@ export class NetworkService { } } - static async scanUser(ipAddress: string): Promise { + static async scanUser(ipAddress: string, ipScanning: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); return await axiosInstance.post('/subnet/devices',{ - subnet: '' + subnet: ipScanning, }); } catch (error: any) { return error.response; @@ -180,7 +180,15 @@ export class NetworkService { static async exportDevices(ipAddress: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); - return await axiosInstance.post(`/settings/export`); + return await axiosInstance.post(`/settings/export_devices`); + } catch (error: any) { + return error.response; + } + } + static async exportNetwork(ipAddress: string): Promise { + try { + const axiosInstance = this.createAxiosInstance(ipAddress); + return await axiosInstance.post(`/settings/export_wifi`); } catch (error: any) { return error.response; } diff --git a/FRONT/DEV/src/components/settings/Settings.tsx b/FRONT/DEV/src/components/settings/Settings.tsx index a086a0d..6cdc1cd 100644 --- a/FRONT/DEV/src/components/settings/Settings.tsx +++ b/FRONT/DEV/src/components/settings/Settings.tsx @@ -146,6 +146,20 @@ export const Settings = ({ onClose }: Props) => { } }; + // Обработка нажатий клавиш + useEffect(() => { + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') + handleSave(); + if (e.key === 'Escape') + handleCancel() + }; + document.addEventListener('keydown', handleKeyPress); + return () => { + document.removeEventListener('keydown', handleKeyPress); + }; + }, []); + return ( diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/IpScanning.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/IpScanning.tsx new file mode 100644 index 0000000..1dc7b4f --- /dev/null +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/IpScanning.tsx @@ -0,0 +1,85 @@ +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) => { + 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) => { + setIpScanning(e.target.value); + } + + return ( + <> + + {!isValid && !loading && ( + {t('ipInput-error')} + )} + + ); +}; + +export default IpScanning; \ No newline at end of file diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx index 9c865bf..513691c 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx @@ -4,6 +4,7 @@ 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; @@ -27,6 +28,7 @@ interface ListUsersProps { } 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'); @@ -55,11 +57,10 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps setCurrentPage(page); } - return ( <> - {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'} + {loading ? t('userScanner-loading') : devices.length > 0 ? t('userScanner-refresh') : t('userScanner-scan')} {devices.length > 0 ? ( @@ -70,7 +71,9 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps }}> {/* Колонка активных устройств */}
-

Активные узлы ({activeDevices.length})

+

+ {t('listUsers-activeNodes')} ({activeDevices.length}) +

{currentActiveDevices.map((device, index) => ( {/* Колонка неактивных устройств */}
-

Неактивные узлы ({inactiveDevices.length})

+

+ {t('listUsers-inactiveNodes')} ({inactiveDevices.length}) +

{currentInactiveDevices.map((device, index) => (
- ) : scanInitiated ? ( -

Узлы не найдены

+ ) : scanInitiated && !loading ? ( +

{t('userScanner-notFound')}

) : null} {totalPages > 1 && ( ` ` export const TableScanner = ({ devices }: TableScannerProps) => { + const {t} = useTranslation('common') const { openModal } = useModalContext(); const nameSetting = useNameSetting(); const ordersColumns = useOrdersColumns(); @@ -96,14 +98,14 @@ export const TableScanner = ({ devices }: TableScannerProps) => { .filter(column => !disabledColumns.includes(ordersColumns.indexOf(column))) .map(column => { switch(column) { - case 'Онлайн': return 'status'; - case 'Имя': return 'name'; - case 'Производитель': return 'vendor'; - case 'IP адрес': return 'ip'; - case 'Открытые порты': return 'ports'; - case 'Последняя активность': return 'activity'; - case 'Заметка': return 'note'; - case 'Действие': return 'action'; + case t('tableScanner-online'): return 'status'; + case t('tableScanner-name'): return 'name'; + case t('tableScanner-vendor'): return 'vendor'; + case t('tableScanner-ip'): return 'ip'; + case t('tableScanner-ports'): return 'ports'; + case t('tableScanner-activity'): return 'activity'; + case t('tableScanner-note'): return 'note'; + case t('tableScanner-action'): return 'action'; default: return ''; } }) @@ -134,7 +136,7 @@ export const TableScanner = ({ devices }: TableScannerProps) => { case 'status': return ; case 'name': - return {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')}; + return {item.hostname || (nameSetting === 'ip' ? item.ip : t('tableScanner-unknown'))}; case 'vendor': return {item.vendor}; case 'ip': @@ -166,12 +168,12 @@ export const TableScanner = ({ devices }: TableScannerProps) => { {visibleColumns.map(column => { switch (column) { case 'status': return ; - case 'name': return Имя; - case 'vendor': return Производитель; - case 'ip': return IP адрес; - case 'ports': return Открытые порты; - case 'activity': return Последняя активность; - case 'note': return Заметка; + case 'name': return {t('tableScanner-name')}; + case 'vendor': return {t('tableScanner-vendor')}; + case 'ip': return {t('tableScanner-ip')}; + case 'ports': return {t('tableScanner-ports')}; + case 'activity': return {t('tableScanner-activity')}; + case 'note': return {t('tableScanner-note')}; case 'action': return ; default: return null; } diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx index 771292e..b548618 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import {DeviceUser} from "../../../../../../store/useDeviceStore"; import {TableScanner} from "./TableScanner"; +import {useTranslation} from "../../../../../../utils/i18n/hooks"; const ScanBtn = styled.button` position: relative; @@ -25,17 +26,18 @@ interface ListUsersProps { } const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => { + const {t} = useTranslation('common') return ( <> - handleScan()} disabled={loading}> - {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'} + handleScan()} disabled={loading}> + {loading ? t('userScanner-loading') : devices.length > 0 ? t('userScanner-refresh') : t('userScanner-scan')} {devices.length > 0 ? ( - ) : scanInitiated ? ( -

Узлы не найдены

+ ) : scanInitiated && !loading ? ( +

{t('userScanner-notFound')}

) : null} ); diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx index dbb8821..6bcdc64 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx @@ -1,12 +1,15 @@ import { useIpStore } from "../../../../../store"; import styled, { keyframes } from "styled-components"; -import {useEffect, useState} from "react"; +import {useEffect, useState, useTransition} from "react"; import { NetworkService } from "../../../../../api/request"; import ListUsers from "./modules/ListUsers"; import TableUsers from "./modules/TableUsers"; import DropdownType from "../../../../UI/Dropdown/DropdownType"; import useNetworkStore from "../../../../../store/useDeviceStore"; import {useIntervalBoolean, useIntervalTime} from "../../../../../hooks/useSettings"; +import IpScanning from "./modules/IpScanning"; +import {useTranslation} from "../../../../../utils/i18n/hooks"; + type Option ={ value: number, @@ -52,7 +55,7 @@ const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>` height: 100%; width: 100%; background: ${(props) => props.theme.background}; - padding: 50px; + padding: 80px 50px 40px; box-sizing: border-box; gap: 24px; transition: justify-content .4s ease; @@ -60,13 +63,20 @@ const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>` overflow-x: hidden; position: relative; `; -const TitleWrapper = styled.h2` +const TopWrapper = styled.div` position: absolute; top: 20px; left: 50%; transform: translateX(-50%); + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +` +const TopTitle = styled.h2` font-size: 32px; width: max-content; + text-align: center; ` const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>` width: 60px; @@ -131,24 +141,38 @@ const ScanningText = styled.span` export default function UserScanner() { + const {t} = useTranslation('common') const { ipAddress } = useIpStore(); + const [ipScanning, setIpScanning] = useState(''); const [loading, setLoading] = useState(false); + const [isValid, setIsValid] = useState(true); const { devices, setDevices } = useNetworkStore(); const [scanInitiated, setScanInitiated] = useState(devices.length > 0); + const validateIp = (ip: string) => { + const regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/([0-9]|[1-2][0-9]|3[0-2]))?$/; + return regex.test(ip); + }; + // Сканирование - const handleScan = async (replay:boolean) => { + const handleScan = async (replay: boolean) => { try { - if(!replay){ + if (ipScanning && !validateIp(ipScanning)) { + setIsValid(false); + return; + } + + setIsValid(true); + + if (!replay) { setLoading(true); } setScanInitiated(true); - const res = await NetworkService.scanUser(ipAddress); - console.log(res) + const res = await NetworkService.scanUser(ipAddress, ipScanning); setDevices(res.data.devices || []); } catch (err) { console.error("Ошибка сканирования сети:", err); - setDevices([]) + setDevices([]); } finally { setLoading(false); } @@ -173,8 +197,8 @@ export default function UserScanner() { // Способ отображения const viewOptions: Option[] = [ - { value: 1, label: 'Таблица' }, - { value: 2, label: 'Список' }, + { value: 1, label: t('userScanner-visible_table') }, + { value: 2, label: t('userScanner-visible_list') }, ]; const [selectedView, setSelectedView] = useState
-