From bce42ff3ffa138bc1a9652807c9899f64760e5d3 Mon Sep 17 00:00:00 2001
From: snegi512 <106768300+snegi512@users.noreply.github.com>
Date: Wed, 12 Mar 2025 22:35:36 +0300
Subject: [PATCH] worked version
---
go.sum | 89 ++++
interface/package-lock.json | 30 ++
interface/package.json | 2 +
interface/src/components/ethernet.jsx | 1 +
.../components/ethernet_components/info.jsx | 2 +-
.../ethernet_components/ip_addres_input.jsx | 133 -----
interface/src/components/menu/menu.jsx | 5 +-
interface/src/components/wlan.jsx | 224 ++++++++-
.../src/components/wlan/form_connect.jsx | 44 --
.../src/components/wlan/hotspot_control.jsx | 49 --
interface/src/components/wlan/info.jsx | 108 ++++
.../src/components/wlan/network_info.jsx | 37 --
.../src/components/wlan/network_list.jsx | 34 --
.../components/wlan/wifi_network_selector.jsx | 165 ++++++
interface/src/components/wlan/wlan.css | 10 -
interface/src/main.jsx | 1 +
interface/vite.config.js | 5 +-
main.go | 162 +-----
models/eth_yaml.go | 27 +
models/interface_IPs_response.go | 8 +
models/mac_address_response.go | 8 +
models/ssid_response.go | 8 +
models/wifi_networks_response.go | 14 +
models/wlan_yaml.go | 25 +
network/eth/handler.go | 90 ++--
network/network.go | 333 -------------
network/wlan/handler.go | 469 ++++++++++++------
wifi.yaml | 21 -
28 files changed, 1074 insertions(+), 1030 deletions(-)
create mode 100644 go.sum
delete mode 100644 interface/src/components/wlan/form_connect.jsx
delete mode 100644 interface/src/components/wlan/hotspot_control.jsx
create mode 100644 interface/src/components/wlan/info.jsx
delete mode 100644 interface/src/components/wlan/network_info.jsx
delete mode 100644 interface/src/components/wlan/network_list.jsx
create mode 100644 interface/src/components/wlan/wifi_network_selector.jsx
delete mode 100644 interface/src/components/wlan/wlan.css
create mode 100644 models/eth_yaml.go
create mode 100644 models/interface_IPs_response.go
create mode 100644 models/mac_address_response.go
create mode 100644 models/ssid_response.go
create mode 100644 models/wifi_networks_response.go
create mode 100644 models/wlan_yaml.go
delete mode 100644 network/network.go
delete mode 100644 wifi.yaml
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7f08abb
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,89 @@
+github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
+github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/interface/package-lock.json b/interface/package-lock.json
index caa18f5..3d7565e 100644
--- a/interface/package-lock.json
+++ b/interface/package-lock.json
@@ -8,10 +8,12 @@
"name": "interface",
"version": "0.0.0",
"dependencies": {
+ "bootstrap-icons": "^1.11.3",
"primeflex": "^4.0.0",
"primeicons": "^7.0.0",
"primereact": "^10.9.2",
"react": "^19.0.0",
+ "react-bootstrap-icons": "^1.11.5",
"react-dom": "^19.0.0",
"sass-embedded": "^1.85.1"
},
@@ -1632,6 +1634,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/bootstrap-icons": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
+ "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/twbs"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/bootstrap"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3816,6 +3834,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-bootstrap-icons": {
+ "version": "1.11.5",
+ "resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.11.5.tgz",
+ "integrity": "sha512-eOhtFJMUqw98IJcfKJsSMZkFHCeNPTTwXZAe9V9d4mT22ARmbrISxPO9GmtWWuf72zQctLeZMGodX/q6wrbYYg==",
+ "license": "MIT",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.6"
+ }
+ },
"node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
diff --git a/interface/package.json b/interface/package.json
index 05a26b1..13f0687 100644
--- a/interface/package.json
+++ b/interface/package.json
@@ -10,10 +10,12 @@
"preview": "vite preview"
},
"dependencies": {
+ "bootstrap-icons": "^1.11.3",
"primeflex": "^4.0.0",
"primeicons": "^7.0.0",
"primereact": "^10.9.2",
"react": "^19.0.0",
+ "react-bootstrap-icons": "^1.11.5",
"react-dom": "^19.0.0",
"sass-embedded": "^1.85.1"
},
diff --git a/interface/src/components/ethernet.jsx b/interface/src/components/ethernet.jsx
index d18d897..2dfb387 100644
--- a/interface/src/components/ethernet.jsx
+++ b/interface/src/components/ethernet.jsx
@@ -19,6 +19,7 @@ export default function Ethernet() {
const [rawYamlContent, setRawYamlContent] = useState(''); // Состояние для хранения raw YAML
+
useEffect(() => {
fetchConfig();
fetchRawConfig(); // Загружаем raw YAML при старте, чтобы быть готовыми к режиму raw edit
diff --git a/interface/src/components/ethernet_components/info.jsx b/interface/src/components/ethernet_components/info.jsx
index a779538..6bb83cc 100644
--- a/interface/src/components/ethernet_components/info.jsx
+++ b/interface/src/components/ethernet_components/info.jsx
@@ -19,7 +19,7 @@ export default function Info({update}){
const macData = await macResponse.json();
setMacAddress(macData);
- const ipResponse = await fetch('/api/eth/eth0ips');
+ const ipResponse = await fetch('/api/eth/ips');
if (!ipResponse.ok) {
throw new Error(`Failed to fetch IP addresses: ${ipResponse.status} ${ipResponse.statusText}`);
}
diff --git a/interface/src/components/ethernet_components/ip_addres_input.jsx b/interface/src/components/ethernet_components/ip_addres_input.jsx
index a49ccc4..30d5c24 100644
--- a/interface/src/components/ethernet_components/ip_addres_input.jsx
+++ b/interface/src/components/ethernet_components/ip_addres_input.jsx
@@ -79,141 +79,8 @@ export default function IPAddressInput({ value, onChange, placeholder = "192.168
{(!isValidIPAddressCIDR(internalValue) && internalValue !== '') ?
: <>>
-
}
- //
);
};
-// eslint-disable-next-line react/prop-types
-// export default function IPAddressInput({ value, onChange }){
-//
-// // Функция для разбора пропса value и установки начального состояния
-// const parseIpAddressValue = useCallback((inputValue) => {
-// if (inputValue) {
-// const parts = inputValue.split('/');
-// const ip = parts[0];
-// const mask = parts[1] || ''; // Маска может быть не указана
-// const ipPartsArray = ip.split('.');
-// return {
-// ipParts: ipPartsArray.length === 4 ? ipPartsArray : ['', '', '', ''],
-// subnetMask: mask,
-// };
-// } else {
-// return { ipParts: ['', '', '', ''], subnetMask: '' };
-// }
-// }, []);
-// const parsedValue = parseIpAddressValue(value)
-//
-// const [ipParts, setIpParts] = useState(parsedValue.ipParts);
-// const [subnetMask, setSubnetMask] = useState(parsedValue.subnetMask);
-//
-//
-//
-// //Эффект для установки начального состояния или обновления при изменении пропса value
-// useEffect(() => {
-// console.log(value)
-// const parsedValue = parseIpAddressValue(value);
-// setIpParts(parsedValue.ipParts);
-// setSubnetMask(parsedValue.subnetMask);
-// // console.log(parsedValue, ipParts, subnetMask);
-// }, [value, parseIpAddressValue]);
-//
-// const handleIpPartChange = useCallback((index, newValue) => {
-// const newIpParts = [...ipParts];
-// newIpParts[index] = newValue;
-// setIpParts(newIpParts);
-// // Вызываем onChange с новым значением IP адреса при изменении части IP
-// if (onChange) {
-// const val = getFullIpAddress({ ipParts, subnetMask: mask })
-// console.log(val)
-// onChange(val); // Передаем текущие части IP и новую маску
-// }
-//
-// }, [ipParts, onChange]);
-// const handleSubnetMaskChange = useCallback((newValue) => {
-// const mask = newValue?newValue.toString():"24"; // Ensure mask is string for consistency
-// setSubnetMask(mask);
-// // Вызываем onChange с новым значением IP адреса при изменении маски
-//
-// if (onChange) {
-// const val = getFullIpAddress({ ipParts, subnetMask: mask })
-// console.log(val)
-// onChange(val); // Передаем текущие части IP и новую маску
-// }
-//
-// }, [onChange, subnetMask]);
-//
-// const isValidIpAddress = useCallback((currentIpParts) => {
-// return currentIpParts.every(part => {
-// const num = parseInt(part, 10);
-// return !isNaN(num) && num >= 0 && num <= 255;
-// });
-// }, []);
-//
-// const isValidSubnetMask = useCallback((currentSubnetMask) => {
-// const num = parseInt(currentSubnetMask, 10);
-// return !isNaN(num) && num >= 0 && num <= 32 && currentSubnetMask !== ''; // Маска не должна быть пустой строкой для валидности
-// }, []);
-//
-// const getFullIpAddress = useCallback(({ ipParts: currentIpParts, subnetMask: currentSubnetMask }) => { // Принимаем части IP и маску как аргументы
-// const validIp = isValidIpAddress(currentIpParts);
-// const validMask = isValidSubnetMask(currentSubnetMask);
-//
-// if (validIp && validMask) {
-// return `${currentIpParts.join('.')}/${currentSubnetMask}`;
-// }
-// return ''; // Или другое значение по умолчанию, если IP или маска невалидны
-// }, [isValidIpAddress, isValidSubnetMask]);
-//
-//
-// const currentFullIp = getFullIpAddress({ ipParts, subnetMask }); // Получаем текущий полный IP адрес для отображения валидации
-//
-// return (
-//
Loading configuration...
;
+ }
+
+
+ // const yamlString = config ? `network:\n version: ${config.network.version}\n renderer: ${config.network.renderer}\n ethernets:\n eth0:\n dhcp4: ${config.network.wifis.wlan0.dhcp4 === undefined ? 'yes' : (config.network.wifis.wlan0.dhcp4 ? 'yes' : 'no')}\n addresses: ${config.network.wifis.wlan0.addresses ? JSON.stringify(config.network.wifis.wlan0.addresses).replace(/,/g, ', ') : '[]'}\n gateway4: ${config.network.wifis.wlan0.gateway4 || 'null'}\n nameservers:\n addresses: ${JSON.stringify(config.network.wifis.wlan0.nameservers.addresses).replace(/,/g, ', ')}` : '';
+
return (
-
-
Управление Wi-Fi
+
+
Управление Wi-Fi
+
+
+
+
+
+
+
+
+
+
+ {!dhcpEnabled ? (
+ <>
+
+
+
+
+
+
+
+
+
+ >) : (<>>)}
+
+
+
{setAccessPoints(e)}} />
+
+
+
+
+ {successMessage && {successMessage}
}
+ {error && Error: {error}
}
+
+
+
);
}
-export default Wlan;
\ No newline at end of file
+export default Wlan;
+
+
+function getSsidAndPassword(data) {
+ try {
+ if (!data?.network?.wifis) {
+ throw new Error("Отсутствует ключ 'wifis' в данных");
+ }
+
+ const wifis = data.network.wifis;
+ const interfaceName = Object.keys(wifis)[0];
+
+ if (!interfaceName || !wifis[interfaceName]?.["access-points"]) {
+ throw new Error("Отсутствует ключ 'access-points' в данных");
+ }
+
+ const accessPoints = wifis[interfaceName]["access-points"];
+ const ssid = Object.keys(accessPoints)[0];
+
+ if (!ssid ) {
+ throw new Error("Отсутствует SSID в данных");
+ }
+ const password = accessPoints[ssid].password||'';
+
+ return {
+ ssid: ssid,
+ password: password
+ };
+ } catch (error) {
+ console.error("Ошибка при извлечении данных:", error.message);
+ return {
+ ssid: "",
+ password: ""
+ };
+ }
+}
\ No newline at end of file
diff --git a/interface/src/components/wlan/form_connect.jsx b/interface/src/components/wlan/form_connect.jsx
deleted file mode 100644
index 4e3e115..0000000
--- a/interface/src/components/wlan/form_connect.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import {useState} from 'react';
-import NetworkList from "./network_list.jsx";
-
-
-const FormConnect = ({apiUrl}) => {
- const [ssid, setSSID] = useState('');
- const [password, setPassword] = useState('');
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- try {
- const response = await fetch(`${apiUrl}/connect`, {
- method: 'POST',
- headers: {'Content-Type': 'application/x-www-form-urlencoded'},
- body: `ssid=${encodeURIComponent(ssid)}&password=${encodeURIComponent(password)}`,
- });
- const result = await response.json();
- alert(result.status);
- } catch (error) {
- alert("Ошибка подключения");
- }
- };
-
- const handleNetworkSelect = (selectedSSID) => {
- setSSID(selectedSSID);
- };
-
- return (
-
-
-
- );
-};
-
-export default FormConnect;
\ No newline at end of file
diff --git a/interface/src/components/wlan/hotspot_control.jsx b/interface/src/components/wlan/hotspot_control.jsx
deleted file mode 100644
index 511884f..0000000
--- a/interface/src/components/wlan/hotspot_control.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { useState } from 'react';
-
-const HotspotControl = ({ apiUrl }) => {
- const [ssid, setSSID] = useState('');
- const [password, setPassword] = useState('');
-
- const enableHotspot = async () => {
- try {
- const response = await fetch(`${apiUrl}/enable-hotspot`, {
- method: 'POST',
- body: `ssid=${ssid}&password=${password}`,
- });
- alert(await response.text());
- } catch (error) {
- alert("Ошибка включения точки доступа");
- }
- };
-
- const disableHotspot = async () => {
- try {
- const response = await fetch(`${apiUrl}/disable-hotspot`);
- alert(await response.text());
- } catch (error) {
- alert("Ошибка отключения точки доступа");
- }
- };
-
- return (
-
-
Точка доступа
- setSSID(e.target.value)}
- />
- setPassword(e.target.value)}
- />
-
-
-
- );
-};
-
-export default HotspotControl;
\ No newline at end of file
diff --git a/interface/src/components/wlan/info.jsx b/interface/src/components/wlan/info.jsx
new file mode 100644
index 0000000..62640df
--- /dev/null
+++ b/interface/src/components/wlan/info.jsx
@@ -0,0 +1,108 @@
+import React, { useState, useEffect } from 'react';
+
+export default function Info(){
+ const [macAddress, setMacAddress] = useState(null);
+ const [ipAddresses, setIpAddresses] = useState(null);
+ const [ssid, setSsid] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchNetworkInfo = async () => {
+ setLoading(true);
+ setError(null);
+
+ try {
+ const macResponse = await fetch('/api/wlan/mac');
+ if (!macResponse.ok) {
+ throw new Error(`Failed to fetch MAC address: ${macResponse.status} ${macResponse.statusText}`);
+ }
+ const macData = await macResponse.json();
+ setMacAddress(macData);
+
+ const ipResponse = await fetch('/api/wlan/ips');
+ if (!ipResponse.ok) {
+ throw new Error(`Failed to fetch IP addresses: ${ipResponse.status} ${ipResponse.statusText}`);
+ }
+ const ipData = await ipResponse.json();
+ setIpAddresses(ipData);
+
+ const ssidResponse = await fetch('/api/wlan/ssid'); // New fetch for SSID
+ // if (!ssidResponse.ok) {
+ // throw new Error(`Failed to fetch SSID (wlan0): ${ssidResponse.status} ${ssidResponse.statusText}`);
+ // }
+ const ssidData = await ssidResponse.json();
+ setSsid(ssidData);
+
+ } catch (err) {
+ setError(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchNetworkInfo();
+
+ // const intervalId = setInterval(() => {
+ // fetchNetworkInfo();
+ // }, 5000);
+
+ // return () => clearInterval(intervalId);
+ }, []);
+
+ if (loading) {
+ return
Загрузка информации о сети...
;
+ }
+
+ if (error) {
+ return (
+
+ Ошибка при загрузке сетевой информации: {error.message}
+ {macAddress?.error &&
Ошибка MAC: {macAddress.error}
}
+ {ipAddresses?.error &&
Ошибка IP: {ipAddresses.error}
}
+ {ssid?.error &&
Ошибка SSID: {ssid.error}
}
+
+ );
+ }
+
+ return (
+
+ {macAddress && macAddress.mac_address ? (
+
MAC-адрес: {macAddress.mac_address}
+ ) : (
+ macAddress && macAddress.error ? (
+
Ошибка получения MAC-адреса: {macAddress.error}
+ ) : (
+
MAC-адрес : Не удалось получить
+ )
+ )}
+ {ssid && ssid.ssid ? (
+
SSID: {ssid.ssid}
+ ) : (
+ ssid && ssid.error ? (
+
Ошибка получения SSID: {ssid.error}
+ ) : (
+
SSID: Не удалось получить
+ )
+ )}
+ {ipAddresses && ipAddresses.ip_addresses && ipAddresses.ip_addresses.length > 0 ? (
+
+
IP-адреса :
+
+ {ipAddresses.ip_addresses.map((ip, index) => (
+ - {ip}
+ ))}
+
+
+ ) : (
+ ipAddresses && ipAddresses.error ? (
+
Ошибка получения IP-адресов: {ipAddresses.error}
+ ) : (
+
IP-адреса: Не найдены
+ )
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/interface/src/components/wlan/network_info.jsx b/interface/src/components/wlan/network_info.jsx
deleted file mode 100644
index 8681790..0000000
--- a/interface/src/components/wlan/network_info.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { useState, useEffect } from 'react';
-
-const NetworkInfo = ({ apiUrl }) => {
- const [info, setInfo] = useState({
- ip: '-',
- netmask: '-',
- gateway: '-',
- mac: '-',
- connected: false,
- });
-
- useEffect(() => {
- const fetchInfo = async () => {
- try {
- const response = await fetch(`${apiUrl}/network-info`);
- const data = await response.json();
- setInfo(data);
- } catch (error) {
- console.error("Ошибка получения информации:", error);
- }
- };
- fetchInfo();
- }, [apiUrl]);
-
- return (
-
-
Сетевая информация:
-
IP: {info.ip}
-
Маска: {info.netmask}
-
Шлюз: {info.gateway}
-
MAC: {info.mac}
-
Статус: {info.connected ? "Подключено" : "Отключено"}
-
- );
-};
-
-export default NetworkInfo;
\ No newline at end of file
diff --git a/interface/src/components/wlan/network_list.jsx b/interface/src/components/wlan/network_list.jsx
deleted file mode 100644
index 0c1563a..0000000
--- a/interface/src/components/wlan/network_list.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { useState, useEffect } from 'react';
-
-const NetworkList = ({ apiUrl, onConnect }) => {
- const [networks, setNetworks] = useState([]);
-
- useEffect(() => {
- const fetchNetworks = async () => {
- try {
- const response = await fetch(`${apiUrl}/list-networks`);
- const data = await response.json();
- setNetworks(data);
- } catch (error) {
- console.error("Ошибка получения списка сетей:", error);
- }
- };
- fetchNetworks();
- }, [apiUrl]);
-
- return (
-
-
Доступные сети:
-
-
- );
-};
-
-export default NetworkList;
\ No newline at end of file
diff --git a/interface/src/components/wlan/wifi_network_selector.jsx b/interface/src/components/wlan/wifi_network_selector.jsx
new file mode 100644
index 0000000..48bb882
--- /dev/null
+++ b/interface/src/components/wlan/wifi_network_selector.jsx
@@ -0,0 +1,165 @@
+import React, {useCallback, useEffect, useRef, useState} from 'react';
+import {FloatLabel} from "primereact/floatlabel";
+import {InputText} from "primereact/inputtext";
+import {Password} from 'primereact/password';
+import {Button} from "primereact/button";
+
+export default function WifiNetworkSelector({value, onChange}) {
+ const [wifiNetworks, setWifiNetworks] = useState([]);
+ const [ssidInput, setSsidInput] = useState('');
+ const [passwordInput, setPasswordInput] = useState('');
+ const [loadingNetworks, setLoadingNetworks] = useState(true);
+ const [networkError, setNetworkError] = useState(null);
+ const passwordInputRef = useRef(null);
+ const fetchWifiNetworks = useCallback(async () => {
+ setLoadingNetworks(true);
+ setNetworkError(null);
+ try {
+ const response = await fetch('/api/wlan/networks');
+ if (!response.ok) {
+ throw new Error(`Failed to fetch WiFi networks: ${response.status} ${response.statusText}`);
+ }
+ const data = await response.json();
+ setWifiNetworks(data.networks || []);
+ } catch (error) {
+ setNetworkError(error);
+ } finally {
+ setLoadingNetworks(false);
+ }
+ }, []);
+
+
+ useEffect(() => {
+ setSsidInput(value.ssid)
+ setPasswordInput(value.password)
+ }, [value]);
+
+
+ const ssidChange = useCallback((e) => {
+ setSsidInput(e);
+ if (onChange) {
+ onChange({
+ password: passwordInput,
+ ssid: e
+ })
+ }
+ });
+ const passwordChange = useCallback((e) => {
+ setPasswordInput(e);
+ if (onChange) {
+ onChange({
+ password: e,
+ ssid: ssidInput
+ })
+ }
+ });
+
+
+ useEffect(() => {
+ fetchWifiNetworks();
+ }, [fetchWifiNetworks]);
+
+ const handleNetworkClick = (ssid) => {
+ setSsidInput(ssid);
+ if (passwordInputRef.current) {
+ passwordInputRef.current.focus();
+ }
+ };
+
+ const handleRefreshClick = () => {
+ fetchWifiNetworks();
+ };
+
+ const getSignalStrengthBars = (signalLevel) => {
+ const level = parseInt(signalLevel, 10);
+ if (isNaN(level)) return 'Нет сигнала';
+
+ const bars = getSignalLevel(signalLevel);
+ let barIcons = '';
+
+ return
+
+ if (barIcons === '') return
;
+ return barIcons;
+ };
+
+ const getSecurityIcon = (securityType) => {
+ if (securityType && securityType.toLowerCase() !== 'open') {
+ return '🔒'; // Locked icon for secured networks
+ }
+ return '🔓'; // Unlocked icon for open networks
+ };
+
+
+ return (
+
+
+
+ {loadingNetworks ? (
+
Загрузка списка WiFi сетей...
+ ) : networkError ? (
+
Ошибка загрузки сетей: {networkError.message}
+ ) : (
+
+ {wifiNetworks.map((network, index) => (
+ - handleNetworkClick(network.ssid)} style={{
+ cursor: 'pointer',
+ padding: '5px',
+ borderBottom: '1px solid #eee',
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center'
+ }}>
+
+ {getSecurityIcon(network.security)} {/* Security Icon */}
+ {network.ssid}
+
+
+ {getSignalStrengthBars(network.signal)}
+
+
+ ))}
+
+ )}
+
+
+
+ ssidChange(e.target.value)}/>
+
+
+
+
+
+
+
+ passwordChange(e.target.value)}
+ ref={passwordInputRef}
+ toggleMask
+ feedback={false}
+ />
+
+
+
+
+ );
+};
+
+
+function getSignalLevel(input) {
+ const [numerator, denominator] = input.split('/').map(Number);
+ if (denominator === 0) {
+ throw new Error("Знаменатель не может быть равен нулю.");
+ }
+ const percentage = (numerator / denominator) * 100;
+ const signalLevel = Math.round((percentage / 100) * 4);
+ return Math.min(Math.max(signalLevel, 0), 4);
+}
\ No newline at end of file
diff --git a/interface/src/components/wlan/wlan.css b/interface/src/components/wlan/wlan.css
deleted file mode 100644
index a93b641..0000000
--- a/interface/src/components/wlan/wlan.css
+++ /dev/null
@@ -1,10 +0,0 @@
-.network-info {
- border: 1px solid #ccc;
- padding: 1rem;
- margin: 1rem 0;
- border-radius: 4px;
-}
-
-.network-info p {
- margin: 0.5rem 0;
-}
\ No newline at end of file
diff --git a/interface/src/main.jsx b/interface/src/main.jsx
index f85044f..ab93bcb 100644
--- a/interface/src/main.jsx
+++ b/interface/src/main.jsx
@@ -4,6 +4,7 @@ import "primereact/resources/themes/arya-purple/theme.css";
import 'primeicons/primeicons.css';
import 'primeflex/primeflex.css';
import 'primereact/resources/primereact.css';
+import 'bootstrap-icons/font/bootstrap-icons.css';
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
diff --git a/interface/vite.config.js b/interface/vite.config.js
index be5c81f..cada411 100644
--- a/interface/vite.config.js
+++ b/interface/vite.config.js
@@ -6,11 +6,12 @@ export default defineConfig({
server: {
proxy: {
'/api': {
- target: 'http://192.168.0.102:8088',
+ target: 'http://192.168.0.208:8088',
changeOrigin: true,
- // rewrite: (path) => path.replace(/^\/api/, ''),
+ rewrite: (path) => path.replace(/^\/net/, ''),
},
},
},
+ base: '/net/',
plugins: [react()],
})
diff --git a/main.go b/main.go
index e4eccd7..d11d105 100644
--- a/main.go
+++ b/main.go
@@ -1,14 +1,10 @@
package main
import (
- "encoding/json"
"log"
"net/http"
- "network_configurator/network"
"network_configurator/network/eth"
"network_configurator/network/wlan"
- "path/filepath"
- "strings"
)
func main() {
@@ -16,159 +12,13 @@ func main() {
http.HandleFunc("/api/eth/netplan", eth.NetplanHandler)
http.HandleFunc("/api/eth/netplan/raw", eth.RawNetplanHandler)
http.HandleFunc("/api/eth/mac", eth.MacAdres)
- http.HandleFunc("/api/eth/eth0ips", eth.InterfaceIPs)
+ http.HandleFunc("/api/eth/ips", eth.InterfaceIPs)
- http.HandleFunc("/api/wlan/connect", wlan.ConnectWiFi)
- http.HandleFunc("/api/wlan/disconnect", wlan.DisconnectWiFi)
- http.HandleFunc("/api/wlan/enable-hotspot", wlan.EnableHotspot)
- http.HandleFunc("/api/wlan/disable-hotspot", wlan.DisableHotspot)
- http.HandleFunc("/api/wlan/set-static-ip", wlan.SetStaticIP)
- http.HandleFunc("/api/wlan/enable-dhcp", wlan.EnableDHCP)
- http.HandleFunc("/api/wlan/list-networks", wlan.ListNetworks)
- http.HandleFunc("/api/wlan/network-info", wlan.GetNetworkInfo)
-
- // Обработчик для сканирования WiFi
- http.HandleFunc("/api/wifi/scan", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- networks, err := network.ScanWiFiNetworks()
- if err != nil {
- log.Printf("Ошибка сканирования WiFi: %v", err)
- network.SendJSONError(w, "Ошибка сканирования WiFi", http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, networks, http.StatusOK)
- })
-
- // Обработчик для подключения к WiFi
- http.HandleFunc("/api/wifi/connect", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- var requestBody map[string]string
- err := json.NewDecoder(r.Body).Decode(&requestBody)
- if err != nil {
- network.SendJSONError(w, "Неверный запрос", http.StatusBadRequest)
- return
- }
-
- ssid := requestBody["ssid"]
- password := requestBody["password"]
-
- if ssid == "" || password == "" {
- network.SendJSONError(w, "Необходимо указать SSID и пароль", http.StatusBadRequest)
- return
- }
-
- err = network.ConnectToWiFi(ssid, password)
- if err != nil {
- log.Printf("Ошибка подключения к WiFi: %v", err)
- network.SendJSONError(w, "Ошибка подключения к WiFi", http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, map[string]string{"status": "success", "message": "Подключение к WiFi инициировано"}, http.StatusOK)
- })
-
- // Обработчик для получения статуса сети
- http.HandleFunc("/api/network/status", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- status, err := network.GetNetworkStatus()
- if err != nil {
- log.Printf("Ошибка получения статуса сети: %v", err)
- network.SendJSONError(w, "Ошибка получения статуса сети", http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, status, http.StatusOK)
- })
-
- // Обработчик для настройки статического IP
- http.HandleFunc("/api/network/configure", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- var requestBody map[string]string
- err := json.NewDecoder(r.Body).Decode(&requestBody)
- if err != nil {
- network.SendJSONError(w, "Неверный запрос", http.StatusBadRequest)
- return
- }
-
- iface := requestBody["interface"]
- ip := requestBody["ip"]
- netmask := requestBody["netmask"]
- gateway := requestBody["gateway"]
-
- if iface == "" || ip == "" || netmask == "" { // Шлюз может быть пустым
- network.SendJSONError(w, "Необходимо указать интерфейс, IP и маску", http.StatusBadRequest)
- return
- }
-
- err = network.ConfigureNetworkInterface(iface, ip, netmask, gateway)
- if err != nil {
- log.Printf("Ошибка конфигурации сети: %v", err)
- network.SendJSONError(w, "Ошибка конфигурации сети: "+err.Error(), http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, map[string]string{"status": "success", "message": "Настройки сети применены"}, http.StatusOK)
- })
-
- // Обработчик для получения текущего SSID подключенной WiFi сети
- http.HandleFunc("/api/wifi/connected-ssid", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- ssid, err := network.GetConnectedWiFiSSID()
- if err != nil {
- log.Printf("Ошибка получения текущего SSID: %v", err)
- // Не отправляем ошибку, если нет подключения, просто возвращаем пустой SSID
- if strings.Contains(err.Error(), "не удалось получить SSID") {
- network.SendJSONResponse(w, map[string]string{"ssid": ""}, http.StatusOK)
- return
- }
- network.SendJSONError(w, "Ошибка получения текущего SSID", http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, map[string]string{"ssid": ssid}, http.StatusOK)
- })
-
- // Обработчик для отключения от WiFi сети
- http.HandleFunc("/api/wifi/disconnect", func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost { // Лучше использовать POST для действий
- network.SendJSONError(w, "Метод не разрешен", http.StatusMethodNotAllowed)
- return
- }
-
- err := network.DisconnectWiFi()
- if err != nil {
- log.Printf("Ошибка отключения от WiFi: %v", err)
- network.SendJSONError(w, "Ошибка отключения от WiFi", http.StatusInternalServerError)
- return
- }
-
- network.SendJSONResponse(w, map[string]string{"status": "success", "message": "Отключение от WiFi инициировано"}, http.StatusOK)
- })
-
- // Обработчик для статики frontend (React build)
- fs := http.FileServer(http.Dir(filepath.Join("frontend", "build")))
- http.Handle("/", fs)
+ http.HandleFunc("/api/wlan/netplan", wlan.NetplanHandler)
+ http.HandleFunc("/api/wlan/mac", wlan.MacAdresWlan0)
+ http.HandleFunc("/api/wlan/ips", wlan.InterfaceIPsWlan0)
+ http.HandleFunc("/api/wlan/ssid", wlan.SSIDWlan0)
+ http.HandleFunc("/api/wlan/networks", wlan.WifiNetworks)
port := ":8088"
log.Printf("Сервер запущен на порту %s", port)
diff --git a/models/eth_yaml.go b/models/eth_yaml.go
new file mode 100644
index 0000000..6d0eda8
--- /dev/null
+++ b/models/eth_yaml.go
@@ -0,0 +1,27 @@
+package models
+
+type Network struct {
+ Version int `yaml:"version" json:"version"`
+ Renderer string `yaml:"renderer" json:"renderer"`
+ Ethernets Ethernets `yaml:"ethernets" json:"ethernets"`
+}
+
+type Ethernets struct {
+ Eth0 Eth0Config `yaml:"eth0" json:"eth0"`
+}
+
+type Eth0Config struct {
+ DHCP4 *bool `yaml:"dhcp4,omitempty" json:"dhcp4"`
+ Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"`
+ Gateway4 string `yaml:"gateway4,omitempty" json:"gateway4,omitempty"`
+ Nameservers Nameservers `yaml:"nameservers,omitempty" json:"nameservers"`
+}
+
+type Nameservers struct {
+ Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"`
+}
+
+// NetplanConfig структура для представления YAML файла
+type NetplanConfig struct {
+ Network Network `yaml:"network" json:"network"`
+}
diff --git a/models/interface_IPs_response.go b/models/interface_IPs_response.go
new file mode 100644
index 0000000..d7fe615
--- /dev/null
+++ b/models/interface_IPs_response.go
@@ -0,0 +1,8 @@
+package models
+
+// InterfaceIPsResponse структура для JSON ответа со списком IP адресов интерфейса
+type InterfaceIPsResponse struct {
+ Interface string `json:"interface"`
+ IPAddresses []string `json:"ip_addresses"`
+ Error string `json:"error,omitempty"`
+}
diff --git a/models/mac_address_response.go b/models/mac_address_response.go
new file mode 100644
index 0000000..e18baef
--- /dev/null
+++ b/models/mac_address_response.go
@@ -0,0 +1,8 @@
+package models
+
+// MacAddressResponse структура для JSON ответа с MAC адресом
+type MacAddressResponse struct {
+ MacAddress string `json:"mac_address"`
+ Interface string `json:"interface"`
+ Error string `json:"error,omitempty"`
+}
diff --git a/models/ssid_response.go b/models/ssid_response.go
new file mode 100644
index 0000000..6132d79
--- /dev/null
+++ b/models/ssid_response.go
@@ -0,0 +1,8 @@
+package models
+
+// SSIDResponse структура для JSON ответа с SSID
+type SSIDResponse struct {
+ SSID string `json:"ssid"`
+ Interface string `json:"interface"`
+ Error string `json:"error,omitempty"`
+}
diff --git a/models/wifi_networks_response.go b/models/wifi_networks_response.go
new file mode 100644
index 0000000..4e2e46c
--- /dev/null
+++ b/models/wifi_networks_response.go
@@ -0,0 +1,14 @@
+package models
+
+// NetworkInfo структура для представления информации о WiFi сети (включая уровень сигнала и тип безопасности)
+type NetworkInfo struct {
+ SSID string `json:"ssid"`
+ Signal string `json:"signal,omitempty"`
+ Security string `json:"security,omitempty"`
+}
+
+// WifiNetworksResponse структура для JSON ответа со списком WiFi сетей
+type WifiNetworksResponse struct {
+ Networks []NetworkInfo `json:"networks"`
+ Error string `json:"error,omitempty"`
+}
diff --git a/models/wlan_yaml.go b/models/wlan_yaml.go
new file mode 100644
index 0000000..3192281
--- /dev/null
+++ b/models/wlan_yaml.go
@@ -0,0 +1,25 @@
+package models
+
+type NetplanConfigWlan struct {
+ Network NetworkWlan `yaml:"network" json:"network"`
+}
+
+type NetworkWlan struct {
+ Version int `yaml:"version" json:"version"`
+ Renderer string `yaml:"renderer" json:"renderer"`
+ Wifis map[string]Wifi `yaml:"wifis" json:"wifis"`
+}
+
+type Wifi struct {
+ Dhcp4 bool `yaml:"dhcp4" json:"dhcp4"`
+ Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"`
+ Gateway4 string `yaml:"gateway4,omitempty" json:"gateway4,omitempty"`
+ Nameservers struct {
+ Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"`
+ } `yaml:"nameservers,omitempty" json:"nameservers,omitempty"`
+ AccessPoints map[string]AccessPoint `yaml:"access-points" json:"access-points"`
+}
+
+type AccessPoint struct {
+ Password string `yaml:"password,omitempty" json:"password,omitempty"`
+}
diff --git a/network/eth/handler.go b/network/eth/handler.go
index 72c96f6..a9896bb 100644
--- a/network/eth/handler.go
+++ b/network/eth/handler.go
@@ -8,37 +8,12 @@ import (
"log"
"net"
"net/http"
+ "network_configurator/models"
"os"
"os/exec"
)
-// NetplanConfig структура для представления YAML файла
-type NetplanConfig struct {
- Network Network `yaml:"network" json:"network"`
-}
-
-type Network struct {
- Version int `yaml:"version" json:"version"`
- Renderer string `yaml:"renderer" json:"renderer"`
- Ethernets Ethernets `yaml:"ethernets" json:"ethernets"`
-}
-
-type Ethernets struct {
- Eth0 Eth0Config `yaml:"eth0" json:"eth0"`
-}
-
-type Eth0Config struct {
- DHCP4 *bool `yaml:"dhcp4,omitempty" json:"dhcp4"` // Используем указатель, чтобы различать "false" и отсутствие ключа
- Addresses []string `yaml:"addresses,omitempty" json:"addresses"`
- Gateway4 string `yaml:"gateway4,omitempty" json:"gateway4,omitempty"`
- Nameservers Nameservers `yaml:"nameservers,omitempty" json:"nameservers"`
-}
-
-type Nameservers struct {
- Addresses []string `yaml:"addresses,omitempty" json:"addresses"`
-}
-
-const netplanFilePath = "/etc/netplan/eth.yaml" // Путь к файлу netplan (для примера, лучше использовать /etc/netplan/eth.yaml)
+const netplanFilePath = "/etc/netplan/eth.yaml"
func NetplanHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@@ -63,7 +38,7 @@ func getNetplanConfig(w http.ResponseWriter, r *http.Request) {
}
func updateNetplanConfig(w http.ResponseWriter, r *http.Request) {
- var updatedConfig NetplanConfig
+ var updatedConfig models.NetplanConfig
err := json.NewDecoder(r.Body).Decode(&updatedConfig)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to decode request body: %v", err), http.StatusBadRequest)
@@ -80,13 +55,13 @@ func updateNetplanConfig(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Netplan configuration updated successfully"))
}
-func readNetplanConfig() (*NetplanConfig, error) {
+func readNetplanConfig() (*models.NetplanConfig, error) {
yamlFile, err := os.ReadFile(netplanFilePath)
if err != nil {
return nil, fmt.Errorf("failed to read netplan file: %w", err)
}
- var config NetplanConfig
+ var config models.NetplanConfig
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal yaml: %w", err)
@@ -95,7 +70,7 @@ func readNetplanConfig() (*NetplanConfig, error) {
return &config, nil
}
-func writeNetplanConfig(config NetplanConfig) error {
+func writeNetplanConfig(config models.NetplanConfig) error {
savedConfig, err := readNetplanConfig()
if err != nil {
return fmt.Errorf("failed to read eth yaml: %w", err)
@@ -178,18 +153,37 @@ func updateRawNetplanConfig(w http.ResponseWriter, r *http.Request) {
}
func readRawNetplanConfig() (string, error) {
+
yamlFile, err := os.ReadFile(netplanFilePath)
if err != nil {
return "", fmt.Errorf("failed to read netplan file: %w", err)
}
+
return string(yamlFile), nil
}
func writeRawNetplanConfig(content string) error {
- err := os.WriteFile(netplanFilePath, []byte(content), 0644)
+
+ savedConfig, err := readRawNetplanConfig()
+ if err != nil {
+ return fmt.Errorf("failed to read eth yaml: %w", err)
+ }
+
+ err = os.WriteFile(netplanFilePath, []byte(content), 0644)
if err != nil {
return fmt.Errorf("failed to write netplan file: %w", err)
}
+
+ cmd := exec.Command("netplan", "apply")
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ err = os.WriteFile(netplanFilePath, []byte(savedConfig), 0644)
+ if err != nil {
+ return fmt.Errorf("failed to write netplan file: %w", err)
+ }
+
+ return fmt.Errorf("Ошибка при выполнении команды: %s\n Вывод команды: %s\n", err, output)
+ }
return nil
}
@@ -227,18 +221,11 @@ func init() {
}
}
-// MacAddressResponse структура для JSON ответа
-type MacAddressResponse struct {
- MacAddress string `json:"mac_address"`
- Interface string `json:"interface"`
- Error string `json:"error,omitempty"`
-}
-
// MacAdres HTTP handler для получения MAC адреса интерфейса eth0
func MacAdres(w http.ResponseWriter, r *http.Request) {
interfaces, err := net.Interfaces()
if err != nil {
- response := MacAddressResponse{
+ response := models.MacAddressResponse{
Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
}
w.Header().Set("Content-Type", "application/json")
@@ -256,7 +243,7 @@ func MacAdres(w http.ResponseWriter, r *http.Request) {
}
if eth0Interface == nil {
- response := MacAddressResponse{
+ response := models.MacAddressResponse{
Error: "Interface eth0 not found",
Interface: "eth0",
}
@@ -268,7 +255,7 @@ func MacAdres(w http.ResponseWriter, r *http.Request) {
macAddress := eth0Interface.HardwareAddr.String()
- response := MacAddressResponse{
+ response := models.MacAddressResponse{
MacAddress: macAddress,
Interface: "eth0",
}
@@ -278,18 +265,11 @@ func MacAdres(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(response)
}
-// InterfaceIPsResponse структура для JSON ответа со списком IP адресов интерфейса
-type InterfaceIPsResponse struct {
- Interface string `json:"interface"`
- IPAddresses []string `json:"ip_addresses"`
- Error string `json:"error,omitempty"`
-}
-
// InterfaceIPs HTTP handler для получения списка IP адресов интерфейса eth0
func InterfaceIPs(w http.ResponseWriter, r *http.Request) {
interfaces, err := net.Interfaces()
if err != nil {
- response := InterfaceIPsResponse{
+ response := models.InterfaceIPsResponse{
Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
}
w.Header().Set("Content-Type", "application/json")
@@ -307,7 +287,7 @@ func InterfaceIPs(w http.ResponseWriter, r *http.Request) {
}
if eth0Interface == nil {
- response := InterfaceIPsResponse{
+ response := models.InterfaceIPsResponse{
Interface: "eth0",
Error: "Interface eth0 not found",
}
@@ -319,7 +299,7 @@ func InterfaceIPs(w http.ResponseWriter, r *http.Request) {
addrs, err := eth0Interface.Addrs()
if err != nil {
- response := InterfaceIPsResponse{
+ response := models.InterfaceIPsResponse{
Interface: "eth0",
Error: fmt.Sprintf("Failed to get addresses for eth0: %v", err),
}
@@ -331,14 +311,14 @@ func InterfaceIPs(w http.ResponseWriter, r *http.Request) {
ipAddresses := make([]string, 0)
for _, addr := range addrs {
- if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { // Проверяем, что это IPNet и не loopback
- if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil { // Проверяем, что это IPv4 или IPv6
+ if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
ipAddresses = append(ipAddresses, ipnet.IP.String())
}
}
}
- response := InterfaceIPsResponse{
+ response := models.InterfaceIPsResponse{
Interface: "eth0",
IPAddresses: ipAddresses,
}
diff --git a/network/network.go b/network/network.go
deleted file mode 100644
index 5386dad..0000000
--- a/network/network.go
+++ /dev/null
@@ -1,333 +0,0 @@
-package network
-
-import (
- "bufio"
- "encoding/json"
- "fmt"
- "net"
- "net/http"
- "os"
- "os/exec"
- "regexp"
- "strconv"
- "strings"
-)
-
-// NetworkInterface представляет информацию о сетевом интерфейсе
-type NetworkInterface struct {
- Name string `json:"name"`
- IP string `json:"ip"`
- Netmask string `json:"netmask"`
- Gateway string `json:"gateway"`
-}
-
-// WiFiNetwork представляет информацию о WiFi сети
-type WiFiNetwork struct {
- SSID string `json:"ssid"`
- Signal int `json:"signal"` // Пример, можно добавить больше информации
- Security string `json:"security"` // Пример, можно добавить больше информации
-}
-
-// ErrorResponse для JSON ответов с ошибками
-type ErrorResponse struct {
- Error string `json:"error"`
-}
-
-// ScanWiFiNetworks сканирует доступные WiFi сети
-func ScanWiFiNetworks() ([]WiFiNetwork, error) {
- networks, err := ScanWiFiNetworksImpl() // Реализация зависит от ОС
- if err != nil {
- return nil, err
- }
- return networks, nil
-}
-
-// ConnectToWiFi подключается к WiFi сети
-func ConnectToWiFi(ssid, password string) error {
- return ConnectToWiFiImpl(ssid, password) // Реализация зависит от ОС
-}
-
-// GetNetworkStatus получает статус сетевых интерфейсов (wlan0, eth0)
-func GetNetworkStatus() ([]NetworkInterface, error) {
- interfaces, err := getNetworkStatusImpl() // Реализация зависит от ОС
- if err != nil {
- return nil, err
- }
- return interfaces, nil
-}
-
-// ConfigureNetworkInterface настраивает статический IP для интерфейса
-func ConfigureNetworkInterface(iface, ip, netmask, gateway string) error {
- return configureNetworkInterfaceImpl(iface, ip, netmask, gateway) // Реализация зависит от ОС
-}
-
-// Функция для выполнения команд shell
-func executeCommand(command string, args ...string) (string, error) {
- cmd := exec.Command(command, args...)
- output, err := cmd.CombinedOutput()
- if err != nil {
- return "", fmt.Errorf("ошибка выполнения команды: %s %s, вывод: %s, ошибка: %v", command, strings.Join(args, " "), string(output), err)
- }
- return string(output), nil
-}
-
-// Функция для отправки JSON ответа
-func SendJSONResponse(w http.ResponseWriter, data interface{}, statusCode int) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(statusCode)
- json.NewEncoder(w).Encode(data)
-}
-
-// Функция для отправки JSON ошибки
-func SendJSONError(w http.ResponseWriter, message string, statusCode int) {
- SendJSONResponse(w, ErrorResponse{Error: message}, statusCode)
-}
-
-// GetConnectedWiFiSSID получает SSID текущей подключенной WiFi сети
-func GetConnectedWiFiSSID() (string, error) {
- ssid, err := getConnectedWiFiSSIDImpl() // Реализация зависит от ОС
- if err != nil {
- return "", err
- }
- return ssid, nil
-}
-
-// DisconnectWiFi отключается от текущей WiFi сети
-func DisconnectWiFi() error {
- return disconnectWiFiImpl() // Реализация зависит от ОС
-}
-
-// ScanWiFiNetworksImpl реализация для Linux
-func ScanWiFiNetworksImpl() ([]WiFiNetwork, error) {
- output, err := executeCommand("sudo", "iwlist", "wlan0", "scan")
- if err != nil {
- return nil, fmt.Errorf("ошибка сканирования WiFi: %v", err)
- }
-
- networks := []WiFiNetwork{}
- scanner := bufio.NewScanner(strings.NewReader(output))
- currentNetwork := WiFiNetwork{}
- reSSID := regexp.MustCompile(`ESSID:"([^"]*)"`)
- reSignal := regexp.MustCompile(`level=(-?\d+)`)
- reEncryption := regexp.MustCompile(`IE: .*?(WPA|WPA2|WEP).*`)
-
- for scanner.Scan() {
- line := scanner.Text()
- if strings.Contains(line, "ESSID:") {
- matches := reSSID.FindStringSubmatch(line)
- if len(matches) > 1 {
- currentNetwork.SSID = matches[1]
- }
- }
- if strings.Contains(line, "Signal level=") {
- matches := reSignal.FindStringSubmatch(line)
- if len(matches) > 1 {
- signalLevel, _ := strconv.Atoi(matches[1])
- currentNetwork.Signal = signalLevel
- }
- }
- if strings.Contains(line, "IE:") && reEncryption.MatchString(line) {
- matches := reEncryption.FindStringSubmatch(line)
- if len(matches) > 1 {
- currentNetwork.Security = matches[1]
- } else {
- currentNetwork.Security = "Open" // Если не найдено, считаем открытой
- }
- }
- if currentNetwork.SSID != "" {
- networks = append(networks, currentNetwork)
- currentNetwork = WiFiNetwork{} // Сброс для следующей сети
- }
- }
-
- return networks, nil
-}
-
-// ConnectToWiFiImpl реализация для Linux
-func ConnectToWiFiImpl(ssid, password string) error {
- // Создаем файл конфигурации wpa_supplicant.conf
- conf := fmt.Sprintf(`ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
-update_config=1
-country=RU
-
-network={
- ssid="%s"
- psk="%s"
-}
-`, ssid, password)
-
- err := os.WriteFile("/etc/wpa_supplicant/wpa_supplicant.conf", []byte(conf), 0600)
- if err != nil {
- return fmt.Errorf("ошибка записи в wpa_supplicant.conf: %v", err)
- }
-
- // Перезапускаем wpa_supplicant
- _, err = executeCommand("sudo", "wpa_cli", "-i", "wlan0", "reconfigure")
- if err != nil {
- // Попробуем перезапустить службу wpa_supplicant, если wpa_cli не сработал
- _, errRestartService := executeCommand("sudo", "systemctl", "restart", "wpa_supplicant")
- if errRestartService != nil {
- return fmt.Errorf("ошибка перезапуска wpa_supplicant: %v, также ошибка wpa_cli: %v", errRestartService, err)
- }
- }
-
- // Переподключаем интерфейс wlan0
- _, err = executeCommand("sudo", "ifdown", "wlan0")
- if err != nil {
- fmt.Printf("Предупреждение: ошибка ifdown wlan0: %v\n", err) // Не критическая ошибка
- }
- _, err = executeCommand("sudo", "ifup", "wlan0")
- if err != nil {
- return fmt.Errorf("ошибка ifup wlan0: %v", err)
- }
-
- return nil
-}
-
-// getNetworkStatusImpl реализация для Linux
-func getNetworkStatusImpl() ([]NetworkInterface, error) {
- interfaces := []NetworkInterface{}
-
- // Получаем статус для eth0
- eth0Status, err := getInterfaceStatus("eth0")
- if err == nil {
- interfaces = append(interfaces, eth0Status)
- }
-
- // Получаем статус для wlan0
- wlan0Status, err := getInterfaceStatus("wlan0")
- if err == nil {
- interfaces = append(interfaces, wlan0Status)
- }
-
- return interfaces, nil
-}
-
-func getInterfaceStatus(ifaceName string) (NetworkInterface, error) {
- ifconfigOutput, err := executeCommand("ifconfig", ifaceName)
- if err != nil {
- return NetworkInterface{}, fmt.Errorf("ошибка ifconfig %s: %v", ifaceName, err)
- }
-
- ip, netmask, err := parseIfconfigOutput(ifconfigOutput)
- if err != nil && !strings.Contains(err.Error(), "interface is down") { // Игнорируем ошибку, если интерфейс выключен
- fmt.Printf("Предупреждение: ошибка парсинга ifconfig %s: %v\n", ifaceName, err)
- return NetworkInterface{Name: ifaceName}, nil // Возвращаем интерфейс без IP и маски, но без критической ошибки
- }
-
- gateway, err := getDefaultGateway(ifaceName)
- if err != nil {
- fmt.Printf("Предупреждение: ошибка получения шлюза для %s: %v\n", ifaceName, err)
- gateway = "" // Шлюз может быть не задан
- }
-
- return NetworkInterface{
- Name: ifaceName,
- IP: ip,
- Netmask: netmask,
- Gateway: gateway,
- }, nil
-}
-
-func parseIfconfigOutput(output string) (ip, netmask string, err error) {
- reIP := regexp.MustCompile(`inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
- reNetmask := regexp.MustCompile(`netmask (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`)
-
- ipMatches := reIP.FindStringSubmatch(output)
- if len(ipMatches) > 1 {
- ip = ipMatches[1]
- } else {
- return "", "", fmt.Errorf("IP адрес не найден или интерфейс выключен") // Изменено сообщение об ошибке
- }
-
- netmaskMatches := reNetmask.FindStringSubmatch(output)
- if len(netmaskMatches) > 1 {
- netmask = netmaskMatches[1]
- } else {
- netmask = "" // Маска может быть не найдена, не критично
- }
-
- return ip, netmask, nil
-}
-
-func getDefaultGateway(ifaceName string) (string, error) {
- routeOutput, err := executeCommand("route", "-n")
- if err != nil {
- return "", fmt.Errorf("ошибка route -n: %v", err)
- }
-
- scanner := bufio.NewScanner(strings.NewReader(routeOutput))
- for scanner.Scan() {
- line := scanner.Text()
- fields := strings.Fields(line)
- if len(fields) >= 4 && fields[0] == "0.0.0.0" && fields[5] == ifaceName {
- return fields[1], nil // Шлюз находится во втором поле
- }
- }
- return "", fmt.Errorf("шлюз по умолчанию для интерфейса %s не найден", ifaceName)
-}
-
-// configureNetworkInterfaceImpl реализация для Linux
-func configureNetworkInterfaceImpl(iface, ip, netmask, gateway string) error {
- // Проверяем валидность IP и маски
- if net.ParseIP(ip) == nil {
- return fmt.Errorf("неверный формат IP адреса: %s", ip)
- }
- if net.ParseIP(netmask) == nil {
- return fmt.Errorf("неверный формат маски подсети: %s", netmask)
- }
-
- // Настраиваем IP и маску
- _, err := executeCommand("sudo", "ifconfig", iface, ip, "netmask", netmask)
- if err != nil {
- return fmt.Errorf("ошибка настройки IP и маски: %v", err)
- }
-
- // Настраиваем шлюз, только если он указан
- if gateway != "" {
- if net.ParseIP(gateway) == nil {
- return fmt.Errorf("неверный формат шлюза: %s", gateway)
- }
- _, err = executeCommand("sudo", "route", "del", "default", "gw", "0.0.0.0", "dev", iface) // Удаляем старый шлюз, если есть
- if err != nil && !strings.Contains(err.Error(), "No such process") && !strings.Contains(err.Error(), "not in table") { // Игнорируем ошибки, если шлюз не был установлен
- fmt.Printf("Предупреждение: ошибка удаления старого шлюза: %v\n", err)
- }
- _, err = executeCommand("sudo", "route", "add", "default", "gw", gateway, "dev", iface)
- if err != nil {
- return fmt.Errorf("ошибка настройки шлюза: %v", err)
- }
- } else {
- // Если шлюз не указан, удаляем шлюз по умолчанию для этого интерфейса
- _, err = executeCommand("sudo", "route", "del", "default", "gw", "0.0.0.0", "dev", iface)
- if err != nil && !strings.Contains(err.Error(), "No such process") && !strings.Contains(err.Error(), "not in table") {
- fmt.Printf("Предупреждение: ошибка удаления шлюза по умолчанию: %v\n", err)
- }
- }
-
- return nil
-}
-
-// getConnectedWiFiSSIDImpl реализация для Linux
-func getConnectedWiFiSSIDImpl() (string, error) {
- output, err := executeCommand("iwconfig", "wlan0")
- if err != nil {
- return "", fmt.Errorf("ошибка выполнения iwconfig wlan0: %v", err)
- }
-
- reSSID := regexp.MustCompile(`ESSID:"([^"]*)"`)
- matches := reSSID.FindStringSubmatch(output)
- if len(matches) > 1 {
- return matches[1], nil
- }
- return "", fmt.Errorf("не удалось получить SSID из вывода iwconfig, возможно, не подключено к WiFi")
-}
-
-// disconnectWiFiImpl реализация для Linux
-func disconnectWiFiImpl() error {
- // Отключаем интерфейс wlan0
- _, err := executeCommand("sudo", "ifdown", "wlan0")
- if err != nil {
- return fmt.Errorf("ошибка ifdown wlan0: %v", err)
- }
- return nil
-}
diff --git a/network/wlan/handler.go b/network/wlan/handler.go
index 6740c7c..af97114 100644
--- a/network/wlan/handler.go
+++ b/network/wlan/handler.go
@@ -3,167 +3,350 @@ package wlan
import (
"encoding/json"
"fmt"
+ "gopkg.in/yaml.v3"
+ "log"
"net"
"net/http"
+ "network_configurator/models"
+ "os"
"os/exec"
- "regexp"
"strings"
)
-type ApiResponse struct {
- Status string `json:"status"`
- Error string `json:"error,omitempty"`
-}
+const netplanFilePath = "/etc/netplan/wlan.yaml"
-type NetworkInfo struct {
- IP string `json:"ip"`
- Netmask string `json:"netmask"`
- Gateway string `json:"gateway"`
- MAC string `json:"mac"`
- Connected bool `json:"connected"`
-}
-
-// Подключение к Wi-Fi
-func ConnectWiFi(w http.ResponseWriter, r *http.Request) {
- ssid := r.FormValue("ssid")
- password := r.FormValue("password")
-
- cmd := exec.Command("nmcli", "dev", "wifi", "connect", ssid, "password", password)
- err := cmd.Run()
+// MacAdresWlan0 HTTP handler для получения MAC адреса интерфейса wlan0
+func MacAdresWlan0(w http.ResponseWriter, r *http.Request) {
+ interfaces, err := net.Interfaces()
if err != nil {
- http.Error(w, "Ошибка подключения", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Отключение Wi-Fi
-func DisconnectWiFi(w http.ResponseWriter, r *http.Request) {
- cmd := exec.Command("nmcli", "dev", "disconnect", "wlan0")
- err := cmd.Run()
- if err != nil {
- http.Error(w, "Ошибка отключения", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Включение точки доступа
-func EnableHotspot(w http.ResponseWriter, r *http.Request) {
- ssid := r.FormValue("ssid")
- password := r.FormValue("password")
-
- cmd := exec.Command("nmcli", "dev", "wifi", "hotspot", "ifname", "wlan0", "ssid", ssid, "password", password)
- err := cmd.Run()
- if err != nil {
- http.Error(w, "Ошибка включения точки доступа", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Отключение точки доступа
-func DisableHotspot(w http.ResponseWriter, r *http.Request) {
- cmd := exec.Command("nmcli", "con", "delete", "Wi-Fi Hotspot")
- err := cmd.Run()
- if err != nil {
- http.Error(w, "Ошибка отключения точки доступа", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Установка статического IP
-func SetStaticIP(w http.ResponseWriter, r *http.Request) {
- ip := r.FormValue("ip")
- gateway := r.FormValue("gateway")
- dns := r.FormValue("dns")
-
- cmd := exec.Command("sh", "-c", fmt.Sprintf(
- "nmcli con modify wlan0 ipv4.method manual ipv4.addresses %s ipv4.gateway %s ipv4.dns %s && nmcli con up wlan0",
- ip, gateway, dns,
- ))
- err := cmd.Run()
- if err != nil {
- http.Error(w, "Ошибка установки статического IP", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Включение DHCP
-func EnableDHCP(w http.ResponseWriter, r *http.Request) {
- cmd := exec.Command("nmcli", "con", "modify", "wlan0", "ipv4.method", "auto")
- err := cmd.Run()
- if err != nil {
- http.Error(w, "Ошибка включения DHCP", http.StatusInternalServerError)
- return
- }
- json.NewEncoder(w).Encode(ApiResponse{Status: "OK"})
-}
-
-// Получение списка сетей
-func ListNetworks(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- cmd := exec.Command("nmcli", "--fields", "SSID,SECURITY,SIGNAL,BARS", "-t", "-f", "SSID,SECURITY,SIGNAL,BARS", "dev", "wifi", "list")
- out, err := cmd.Output()
- if err != nil {
- http.Error(w, "Ошибка получения списка сетей", http.StatusInternalServerError)
- return
- }
- networks := parseNetworks(string(out))
- json.NewEncoder(w).Encode(networks)
-}
-
-func parseNetworks(output string) []map[string]string {
- lines := strings.Split(output, "\n")[1:]
- networks := make([]map[string]string, 0)
- for _, line := range lines {
- if line == "" {
- continue
+ response := models.MacAddressResponse{
+ Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
}
- fields := strings.Split(line, ":")
- network := make(map[string]string)
- network["ssid"] = fields[0]
- network["security"] = fields[1]
- network["signal"] = fields[2]
- network["bars"] = fields[3]
- networks = append(networks, network)
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(response)
+ return
}
- return networks
+
+ var wlan0Interface *net.Interface
+ for _, iface := range interfaces {
+ if iface.Name == "wlan0" {
+ wlan0Interface = &iface
+ break
+ }
+ }
+
+ if wlan0Interface == nil {
+ response := models.MacAddressResponse{
+ Error: "Interface wlan0 not found",
+ Interface: "wlan0",
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusNotFound)
+ json.NewEncoder(w).Encode(response)
+ return
+ }
+
+ macAddress := wlan0Interface.HardwareAddr.String()
+
+ response := models.MacAddressResponse{
+ MacAddress: macAddress,
+ Interface: "wlan0",
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(response)
}
-// Информация о текущем соединении
-func GetNetworkInfo(w http.ResponseWriter, r *http.Request) {
- info := NetworkInfo{Connected: false}
-
- // MAC
- outMac, _ := exec.Command("ip", "link", "show", "wlan0").Output()
- macRegex := regexp.MustCompile(`link/ether ([0-9a-f:]+)`)
- if match := macRegex.FindStringSubmatch(string(outMac)); len(match) > 1 {
- info.MAC = match[1]
+// InterfaceIPsWlan0 HTTP handler для получения списка IP адресов интерфейса wlan0
+func InterfaceIPsWlan0(w http.ResponseWriter, r *http.Request) {
+ interfaces, err := net.Interfaces()
+ if err != nil {
+ response := models.InterfaceIPsResponse{
+ Error: fmt.Sprintf("Failed to get network interfaces: %v", err),
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(response)
+ return
}
- // IP и маска
- outIP, _ := exec.Command("ip", "-4", "addr", "show", "wlan0").Output()
- ipRegex := regexp.MustCompile(`inet ([0-9.]+)/[0-9]+`)
- if matchIP := ipRegex.FindStringSubmatch(string(outIP)); len(matchIP) > 1 {
- info.IP = matchIP[1]
+ var wlan0Interface *net.Interface
+ for _, iface := range interfaces {
+ if iface.Name == "wlan0" {
+ wlan0Interface = &iface
+ break
+ }
}
- netmaskRegex := regexp.MustCompile(`inet [0-9.]+/[0-9]+`)
- if matchNetmask := netmaskRegex.FindStringSubmatch(string(outIP)); len(matchNetmask) > 0 {
- _, ipNet, _ := net.ParseCIDR(matchNetmask[0])
- info.Netmask = ipNet.Mask.String()
+ if wlan0Interface == nil {
+ response := models.InterfaceIPsResponse{
+ Interface: "wlan0",
+ Error: "Interface wlan0 not found",
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusNotFound)
+ json.NewEncoder(w).Encode(response)
+ return
}
- // Шлюз
- outGateway, _ := exec.Command("ip", "route", "show", "default").Output()
- gatewayRegex := regexp.MustCompile(`default via ([0-9.]+)`)
- if matchGateway := gatewayRegex.FindStringSubmatch(string(outGateway)); len(matchGateway) > 1 {
- info.Gateway = matchGateway[1]
+ addrs, err := wlan0Interface.Addrs()
+ if err != nil {
+ response := models.InterfaceIPsResponse{
+ Interface: "wlan0",
+ Error: fmt.Sprintf("Failed to get addresses for wlan0: %v", err),
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(response)
+ return
}
- info.Connected = info.IP != "" && info.Gateway != ""
- json.NewEncoder(w).Encode(info)
+ ipAddresses := make([]string, 0)
+ for _, addr := range addrs {
+ if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
+ ipAddresses = append(ipAddresses, ipnet.IP.String())
+ }
+ }
+ }
+
+ response := models.InterfaceIPsResponse{
+ Interface: "wlan0",
+ IPAddresses: ipAddresses,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(response)
+}
+
+// SSIDWlan0 HTTP handler для получения SSID интерфейса wlan0
+func SSIDWlan0(w http.ResponseWriter, r *http.Request) {
+ // Command to get SSID (Linux - iwgetid, macOS - airport, Windows - needs different approach)
+ cmd := "iwgetid" // For Linux - you might need to install 'wireless-tools' package
+ out, err := exec.Command(cmd, "wlan0", "-r").Output()
+ if err != nil {
+ response := models.SSIDResponse{
+ Interface: "wlan0",
+ Error: fmt.Sprintf("Failed to get SSID using %s: %v", cmd, err),
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(response)
+ return
+ }
+
+ ssid := strings.TrimSpace(string(out))
+
+ if ssid == "" {
+ response := models.SSIDResponse{
+ Interface: "wlan0",
+ Error: "SSID is empty or not connected",
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusNotFound)
+ json.NewEncoder(w).Encode(response)
+ return
+ }
+
+ response := models.SSIDResponse{
+ Interface: "wlan0",
+ SSID: ssid,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(response)
+}
+
+func createTestNetplanFile() {
+ initialYaml := `
+network:
+ version: 2
+ renderer: networkd
+ wifis:
+ wlan0:
+ dhcp4: yes
+`
+ err := os.WriteFile(netplanFilePath, []byte(initialYaml), 0644)
+ if err != nil {
+ log.Fatalf("Failed to create test netplan file: %v", err)
+ }
+ fmt.Println("Test netplan file created at:", netplanFilePath)
+}
+
+func init() {
+ if _, err := os.Stat(netplanFilePath); os.IsNotExist(err) {
+ createTestNetplanFile()
+ }
+}
+
+func NetplanHandler(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case http.MethodGet:
+ getNetplanConfig(w, r)
+ case http.MethodPut:
+ updateNetplanConfig(w, r)
+ default:
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ }
+}
+
+func getNetplanConfig(w http.ResponseWriter, r *http.Request) {
+ config, err := readNetplanConfig()
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Failed to read config: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(config)
+}
+
+func updateNetplanConfig(w http.ResponseWriter, r *http.Request) {
+ var updatedConfig models.NetplanConfigWlan
+ err := json.NewDecoder(r.Body).Decode(&updatedConfig)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Failed to decode request body: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ err = writeNetplanConfig(updatedConfig)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Failed to write config: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("Netplan configuration updated successfully"))
+}
+
+func readNetplanConfig() (*models.NetplanConfigWlan, error) {
+ yamlFile, err := os.ReadFile(netplanFilePath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read netplan file: %w", err)
+ }
+
+ var config models.NetplanConfigWlan
+ err = yaml.Unmarshal(yamlFile, &config)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal yaml: %w", err)
+ }
+
+ return &config, nil
+}
+
+func writeNetplanConfig(config models.NetplanConfigWlan) error {
+ savedConfig, err := readNetplanConfig()
+ if err != nil {
+ return fmt.Errorf("failed to read eth yaml: %w", err)
+ }
+
+ yamlData, err := yaml.Marshal(config)
+ if err != nil {
+ return fmt.Errorf("failed to marshal yaml: %w", err)
+ }
+
+ err = os.WriteFile(netplanFilePath, yamlData, 0644)
+ if err != nil {
+ return fmt.Errorf("failed to write netplan file: %w", err)
+ }
+
+ cmd := exec.Command("netplan", "apply")
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+
+ yamlData, err := yaml.Marshal(*savedConfig)
+ if err != nil {
+ return fmt.Errorf("failed to marshal yaml: %w", err)
+ }
+
+ err = os.WriteFile(netplanFilePath, yamlData, 0644)
+ if err != nil {
+ return fmt.Errorf("failed to write netplan file: %w", err)
+ }
+
+ return fmt.Errorf("Ошибка при выполнении команды: %s\n Вывод команды: %s\n", err, output)
+ }
+
+ return nil
+}
+
+// WifiNetworks HTTP handler для получения списка видимых WiFi сетей с уровнем сигнала и типом безопасности
+func WifiNetworks(w http.ResponseWriter, r *http.Request) {
+ interfaceName := "wlan0" // Жестко задан интерфейс wlan0 для сканирования
+ cmd := "iwlist" // Using 'iwlist' command to scan WiFi networks (Linux specific)
+ out, err := exec.Command(cmd, interfaceName, "scan").Output()
+ if err != nil {
+ response := models.WifiNetworksResponse{
+ Error: fmt.Sprintf("Failed to scan WiFi networks using %s: %v", cmd, err),
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusInternalServerError)
+ json.NewEncoder(w).Encode(response)
+ return
+ }
+
+ output := string(out)
+ lines := strings.Split(output, "\n")
+ networks := make([]models.NetworkInfo, 0)
+ seenSSIDs := make(map[string]bool)
+
+ var currentNetworkInfo models.NetworkInfo
+ var signal string
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+
+ if strings.Contains(line, "Signal level=") {
+ signalLevel := strings.SplitN(line, "=", 2)[1]
+ signalLevel = strings.Fields(signalLevel)[0]
+ signal = signalLevel
+ }
+
+ if strings.Contains(line, "ESSID:") {
+ ssid := strings.SplitN(line, ":", 2)[1]
+ ssid = strings.Trim(ssid, "\"")
+ if !seenSSIDs[ssid] {
+ if currentNetworkInfo.SSID != "" {
+ networks = append(networks, currentNetworkInfo)
+ }
+ currentNetworkInfo = models.NetworkInfo{SSID: ssid, Signal: signal}
+ seenSSIDs[ssid] = true
+ }
+ } else if strings.Contains(line, "Encryption key:") && strings.Contains(line, "off") {
+ currentNetworkInfo.Security = "Open" // No encryption, Open network
+ } else if strings.Contains(line, "IE:") { // Information Element - for security type
+ if strings.Contains(line, "WPA2") {
+ currentNetworkInfo.Security = "WPA2"
+ } else if strings.Contains(line, "WPA") {
+ currentNetworkInfo.Security = "WPA"
+ } else if strings.Contains(line, "WEP") {
+ currentNetworkInfo.Security = "WEP"
+ } else {
+ if currentNetworkInfo.Security == "" { // If no specific WPA/WEP found and not marked as Open
+ currentNetworkInfo.Security = "Secured" // Generic secured if IE present but type not identified
+ }
+ }
+ }
+ }
+ if currentNetworkInfo.SSID != "" {
+ networks = append(networks, currentNetworkInfo)
+ }
+
+ // If Security is still empty after all checks, and it's not Open, assume it's some form of Secured
+ for i := range networks {
+ if networks[i].Security == "" && networks[i].SSID != "" {
+ networks[i].Security = "Secured" // Default to Secured if type not determined by IE and not explicitly Open
+ }
+ }
+
+ response := models.WifiNetworksResponse{
+ Networks: networks,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(response)
}
diff --git a/wifi.yaml b/wifi.yaml
deleted file mode 100644
index 5b7aacd..0000000
--- a/wifi.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-network:
- version: 2
- renderer: networkd
- wifis:
- wlan0:
- mode: ap
- access-points:
- my-ap:
- password: "your_strong_password"
- security: wpa2-personal
- dhcp4: yes
- dhcp4-subnet: 192.168.10.0/24
- bridges:
- br0:
- interfaces: [ eth0, wlan0 ]
- dhcp4: yes # Получить IP-адрес для моста от DHCP-сервера в проводной сети
- gateway4: default # Использовать шлюз, полученный по DHCP
- nameservers:
- addresses: [ 8.8.8.8, 8.8.4.4 ] # Пример DNS-серверов
- parameters:
- forward-delay: 0 # Ускорить процесс моста
\ No newline at end of file