This commit is contained in:
2026-04-04 00:09:02 +08:00
commit 38e896363e
117 changed files with 119311 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func (a *App) buildUI() {
a.statusLabel = widget.NewLabel("Готов к работе")
a.statusLabel.TextStyle = fyne.TextStyle{Bold: true}
a.fileInfo = widget.NewLabel("Файл не выбран")
a.version = widget.NewLabel("2603-01.8")
a.progressBar = widget.NewProgressBar()
a.progressBar.Hide()
a.serverTab = container.NewAppTabs()
a.platformTab = container.NewAppTabs()
// a.contentArea = container.NewHBox()
uploadBtnServer := widget.NewButton("🖥 server", a.onUploadServer)
// uploadBtnServer.Importance = widget.HighImportance
uploadBtnPlatform := widget.NewButton("🖧 platform", a.onUploadPlatform)
// Панель инструментов
toolbar := container.NewHBox(
uploadBtnServer,
uploadBtnPlatform,
widget.NewSeparator(),
a.progressBar,
)
// Основной контент
mainContent := container.NewBorder(
container.NewHBox(
toolbar,
widget.NewSeparator(),
a.statusLabel,
),
container.NewHBox(
widget.NewSeparator(),
a.fileInfo,
a.version,
),
nil, nil,
container.NewAppTabs(
container.NewTabItem("Server", a.serverTab),
container.NewTabItem("Platform", a.platformTab),
),
)
a.window.SetContent(mainContent)
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,39 @@
module dci6-support-reader
go 1.24.4
require (
fyne.io/fyne v1.4.3 // indirect
fyne.io/fyne/v2 v2.7.1 // indirect
fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fyne-io/gl-js v0.2.0 // indirect
github.com/fyne-io/glfw-js v0.3.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect
github.com/fyne-io/oksvg v0.2.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-text/render v0.2.0 // indirect
github.com/go-text/typesetting v0.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.2 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
golang.org/x/image v0.24.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -0,0 +1,119 @@
fyne.io/fyne v1.4.3 h1:356CnXCiYrrfaLGsB7qLK3c6ktzyh8WR05v/2RBu51I=
fyne.io/fyne v1.4.3/go.mod h1:8kiPBNSDmuplxs9WnKCkaWYqbcXFy0DeAzwa6PBO9Z8=
fyne.io/fyne/v2 v2.7.1 h1:ja7rNHWWEooha4XBIZNnPP8tVFwmTfwMJdpZmLxm2Zc=
fyne.io/fyne/v2 v2.7.1/go.mod h1:xClVlrhxl7D+LT+BWYmcrW4Nf+dJTvkhnPgji7spAwE=
fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 h1:eA5/u2XRd8OUkoMqEv3IBlFYSruNlXD8bRHDiqm0VNI=
fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
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/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/mobile v0.1.2/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,55 @@
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
type App struct {
app fyne.App
window fyne.Window
serverContent []byte
platformContent []byte
fileName string
filePath string
fileSize int64
// Виджеты, которые часто обновляем
statusLabel *widget.Label
fileInfo *widget.Label
version *widget.Label
progressBar *widget.ProgressBar
contentArea *fyne.Container
serverTab *container.AppTabs
platformTab *container.AppTabs
}
var iconData = []byte{0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x04, 0x03, 0x00, 0x00, 0x00, 0x81, 0x54, 0x67, 0xc7, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, 0x00, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x93, 0xd6, 0xff, 0x90, 0xd5, 0xff, 0x91, 0xd5, 0xff, 0x92, 0xd6, 0xff, 0xae, 0xe0, 0xff, 0xf2, 0xfa, 0xff, 0xfb, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xef, 0xff, 0xd9, 0xf0, 0xff, 0xd1, 0xed, 0xff, 0xfc, 0xfe, 0xff, 0xa0, 0xdb, 0xff, 0xa7, 0xde, 0xff, 0xc0, 0xe7, 0xff, 0x52, 0x19, 0x63, 0x47, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x37, 0x5c, 0x00, 0x00, 0x37, 0x5c, 0x01, 0xcb, 0xc7, 0xa4, 0xb9, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xea, 0x02, 0x0e, 0x09, 0x10, 0x08, 0xaa, 0xdc, 0x17, 0xfb, 0x00, 0x00, 0x00, 0xe2, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x6d, 0x92, 0xbd, 0x0d, 0xc2, 0x30, 0x10, 0x85, 0x1f, 0x47, 0x45, 0x87, 0xa3, 0x0c, 0xe0, 0x38, 0x80, 0x04, 0x08, 0x84, 0xb2, 0x41, 0x50, 0x26, 0x40, 0x62, 0x01, 0x0a, 0x60, 0x02, 0x68, 0xa8, 0x11, 0x25, 0x62, 0x04, 0x46, 0x60, 0x09, 0x7a, 0x7a, 0x1a, 0x16, 0x81, 0x3b, 0xe3, 0x38, 0xc8, 0xe6, 0x2b, 0x2c, 0xbf, 0x17, 0xfb, 0x7c, 0x3f, 0x41, 0x4b, 0x31, 0xb9, 0xd2, 0xa4, 0x0a, 0xd9, 0x75, 0x21, 0xab, 0x21, 0x31, 0xb4, 0x91, 0xbd, 0x35, 0x34, 0x34, 0x40, 0x20, 0x67, 0xa4, 0x6a, 0x06, 0x4b, 0x5b, 0x95, 0xd6, 0xd0, 0x06, 0x30, 0x0c, 0xaf, 0x24, 0x46, 0xca, 0xdf, 0x72, 0x39, 0x5c, 0xf0, 0xa6, 0xb4, 0x27, 0x60, 0xa3, 0x71, 0x6c, 0xd0, 0xf7, 0x0a, 0x55, 0x0e, 0x2d, 0x46, 0x92, 0xa3, 0xbf, 0x74, 0x2c, 0x50, 0x64, 0x48, 0x80, 0xcd, 0xde, 0xb1, 0x03, 0x32, 0xce, 0x80, 0x4e, 0x1e, 0x16, 0x28, 0xd0, 0x69, 0x8c, 0x33, 0x72, 0x28, 0x3d, 0x6a, 0x8c, 0x2b, 0xf1, 0x2b, 0xf8, 0x35, 0x58, 0x1a, 0x8c, 0xef, 0x75, 0xd0, 0xc3, 0x8d, 0x25, 0x30, 0x59, 0x7a, 0x1e, 0xb6, 0xa6, 0x5e, 0xe5, 0x99, 0xb3, 0x34, 0x98, 0xbe, 0x3d, 0x2f, 0x96, 0x0a, 0xc3, 0x26, 0xe8, 0x85, 0x65, 0xf4, 0x6c, 0x94, 0x58, 0x94, 0x3a, 0x17, 0xb7, 0xad, 0xf5, 0x51, 0x8a, 0xe3, 0xf2, 0x07, 0x6b, 0xc7, 0x4a, 0xca, 0x97, 0x06, 0x3d, 0x1d, 0xda, 0x75, 0x2c, 0x68, 0xe1, 0x9f, 0x26, 0x07, 0x63, 0x88, 0x07, 0x15, 0x8d, 0x32, 0x18, 0x76, 0xf8, 0x3b, 0x7c, 0x00, 0x1a, 0xb4, 0x74, 0x9d, 0x66, 0xa4, 0x3d, 0x9a, 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x00, 0x32, 0x30, 0x32, 0x36, 0x2d, 0x30, 0x32, 0x2d, 0x31, 0x34, 0x54, 0x30, 0x38, 0x3a, 0x33, 0x32, 0x3a, 0x33, 0x35, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x42, 0xed, 0xf0, 0x52, 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x00, 0x32, 0x30, 0x32, 0x36, 0x2d, 0x30, 0x31, 0x2d, 0x31, 0x34, 0x54, 0x31, 0x34, 0x3a, 0x33, 0x39, 0x3a, 0x31, 0x35, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2d, 0x96, 0x6a, 0x40, 0x00, 0x00, 0x00, 0x28, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x00, 0x32, 0x30, 0x32, 0x36, 0x2d, 0x30, 0x32, 0x2d, 0x31, 0x34, 0x54, 0x30, 0x39, 0x3a, 0x31, 0x36, 0x3a, 0x30, 0x38, 0x2b, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x07, 0xec, 0xcf, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}
func NewApp() *App {
a := &App{
app: app.NewWithID("com.ispsystem.dci6-support-reader"),
}
a.window = a.app.NewWindow("ISPSystem Техподдержка DCImanager6")
// icon, err := fyne.LoadResourceFromPath("./dci6-support-reader.jpg")
// if err != nil {
// log.Printf("Загрузка иконки: %s", err)
// }
icon := fyne.NewStaticResource("dci6-support-reader.png", iconData)
a.window.SetIcon(icon)
return a
}
func (a *App) Run() {
a.buildUI()
a.window.Resize(fyne.NewSize(1200, 600))
a.window.ShowAndRun()
}
func main() {
app := NewApp()
app.Run()
}

View File

@@ -0,0 +1,235 @@
package main
import (
"fmt"
"path/filepath"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
)
// Вспомогательные методы
func (a *App) showError(title, message string) {
dialog.ShowError(fmt.Errorf(message), a.window)
a.statusLabel.SetText("❌ " + title)
}
func (a *App) showSuccess(title, message string) {
dialog.ShowInformation(title, message, a.window)
}
func (a *App) detectMimeType() string {
ext := strings.ToLower(filepath.Ext(a.fileName))
mimeTypes := map[string]string{
".txt": "text/plain",
".pdf": "application/pdf",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".zip": "application/zip",
".doc": "application/msword",
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
}
if mime, ok := mimeTypes[ext]; ok {
return mime
}
return "application/octet-stream"
}
// // Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne
type JournalEntryWidget struct {
widget.BaseWidget
event journalctlEntry
label *widget.Label
}
func NewJournalEntryWidget(event journalctlEntry) *JournalEntryWidget {
j := &JournalEntryWidget{
event: event,
}
j.ExtendBaseWidget(j)
return j
}
func (j *JournalEntryWidget) CreateRenderer() fyne.WidgetRenderer {
j.label = widget.NewLabel(j.formatEventText())
j.label.Wrapping = fyne.TextWrapBreak
j.label.Selectable = true
return widget.NewSimpleRenderer(j.label)
}
func (j *JournalEntryWidget) formatEventText() string {
return fmt.Sprintf("%s :: %s :: %s :: %s :: %s",
j.event.Timestamp, j.event.CommandLine, j.event.CommandName,
j.event.SyslogIdentifier, j.event.Message)
}
func (j *JournalEntryWidget) UpdateEvent(event journalctlEntry) {
j.event = event
if j.label != nil {
j.label.SetText(j.formatEventText())
}
j.Refresh()
}
//
//
//
//
// func (a *App) isTextFile() bool {
// ext := strings.ToLower(filepath.Ext(a.fileName))
// textExts := []string{".txt", ".csv", ".json", ".xml", ".yaml", ".yml", ".md", ".go", ".py", ".js"}
// for _, textExt := range textExts {
// if ext == textExt {
// return true
// }
// }ц=
// // Проверяем содержимое (простейшая проверка)
// if len(a.fileContent) == 0 {
// return false
// }
// // Если файл содержит null-байты, это скорее бинарный файл
// for _, b := range a.fileContent {
// if b == 0 {
// return false
// }
// }
// return true
// }
// type TextStats struct {
// lines int
// words int
// chars int
// bytes int
// }
// func (a *App) calculateTextStats() TextStats {
// if len(a.fileContent) == 0 {
// return TextStats{}
// }
// content := string(a.fileContent)
// stats := TextStats{
// bytes: len(a.fileContent),
// chars: len(content),
// lines: strings.Count(content, "\n") + 1,
// words: len(strings.Fields(content)),
// }
// return stats
// }
// Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne
// Кастомный виджет для элемента списка с динамической высотой
type JournalListItem struct {
widget.BaseWidget
event journalctlEntry
label *widget.Label
}
func NewJournalListItem(event journalctlEntry) *JournalListItem {
j := &JournalListItem{
event: event,
}
j.ExtendBaseWidget(j)
return j
}
func (j *JournalListItem) CreateRenderer() fyne.WidgetRenderer {
j.label = widget.NewLabel(j.formatText())
j.label.Wrapping = fyne.TextWrapBreak
j.label.Selectable = true
return &journalListItemRenderer{
item: j,
label: j.label,
}
}
func (j *JournalListItem) formatText() string {
return fmt.Sprintf("%s :: %s :: %s :: %s :: %s",
j.event.Timestamp, j.event.CommandLine, j.event.CommandName,
j.event.SyslogIdentifier, j.event.Message)
}
func (j *JournalListItem) Update(event journalctlEntry) {
j.event = event
if j.label != nil {
j.label.SetText(j.formatText())
}
j.Refresh()
}
type journalListItemRenderer struct {
item *JournalListItem
label *widget.Label
}
func (r *journalListItemRenderer) Layout(size fyne.Size) {
// Рассчитываем высоту label на основе его содержимого
minSize := r.label.MinSize()
labelHeight := minSize.Height
// Если высота label больше 50, используем её, иначе минимальную
height := labelHeight
if height < 50 {
height = 50
}
r.label.Resize(fyne.NewSize(size.Width, height))
r.label.Move(fyne.NewPos(0, 0))
}
func (r *journalListItemRenderer) MinSize() fyne.Size {
minSize := r.label.MinSize()
if minSize.Height < 50 {
minSize.Height = 50
}
return minSize
}
func (r *journalListItemRenderer) Refresh() {
r.label.Refresh()
}
func (r *journalListItemRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{r.label}
}
func (r *journalListItemRenderer) Destroy() {}
//
//
//
//
//
//
//
//
//
//
// Вспомогательные функции
func formatFileSize(bytes int64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d Б", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cБ", float64(bytes)/float64(div), "КМГТП"[exp])
}

View File

@@ -0,0 +1,74 @@
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func createNetworkTab(server outputStruct) *container.AppTabs {
etcHostsList := widget.NewList(
func() int {
return len(server.NetworkConfig.EtcHosts)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("etc hosts"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
line := server.NetworkConfig.EtcHosts[id]
lineLabel := hbox.Objects[0].(*widget.Label)
lineLabel.SetText(line)
},
)
netInterfacesList := widget.NewList(
//TODO тут всё может упасть. В NetInterfaces лежит структура, в которой есть список ip адресов
// обычно их не очень много, но в крайних случаях их может быть 100500 на сетевом интерфейсе
// и гуй этому не обрадуется. Пока фигачим всё одной строкой
func() int {
return len(server.NetworkConfig.NetInterfaces)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("интерфейс"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
iface := server.NetworkConfig.NetInterfaces[id]
ifaceLabel := hbox.Objects[0].(*widget.Label)
ifaceLabel.SetText(fmt.Sprintf("%s, флаги: %s, мак: %s, ip: %s, index: %s, MTU: %s", iface.Device, iface.Flags, iface.HwAddr, iface.IpAddr, iface.Index, iface.MTU))
},
)
networkManagerConnList := widget.NewList(
func() int {
return len(server.NetworkConfig.NetworkManagerConn)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("соединение"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
conn := server.NetworkConfig.NetworkManagerConn[id]
connLabel := hbox.Objects[0].(*widget.Label)
connLabel.SetText(conn)
},
)
networkAppTabs := container.NewAppTabs()
networkAppTabs.Append(container.NewTabItem("etc/hosts", container.NewScroll(etcHostsList)))
networkAppTabs.Append(container.NewTabItem("Список сетевых интерфейсов", container.NewScroll(netInterfacesList)))
networkAppTabs.Append(container.NewTabItem("nmcli c s", container.NewScroll(networkManagerConnList)))
networkAppTabs.Append(container.NewTabItem("NetworkManager", widget.NewLabel(fmt.Sprintf("Включен: %s", server.NetworkConfig.NetworkManager))))
networkAppTabs.Append(container.NewTabItem("Networking", widget.NewLabel(fmt.Sprintf("Включен: %s", server.NetworkConfig.Networking))))
return networkAppTabs
}

View File

@@ -0,0 +1,9 @@
### Упаковка с помощью fyne-cross
используется docker, так что он должен быть установлен и настроен.
fyne-cross windows -icon ./dci6-support-reader.png -name dci6-support-reader -app-id com.ispsystem.dci6-suppor-reader
fyne-cross linux -icon ./dci6-support-reader.png -name dci6-support-reader -app-id com.ispsystem.dci6-suppor-reader
прочитать содержимое png в байтах, что бы засунуть сразу в переменную для иконки
xxd -i ./dci6-support-reader.png | head -n -1 | tail -n +2 | sed 's/ /, /g' | tr -d '\n' | sed 's/^/var iconData = []byte{/;s/$/}/'

View File

@@ -0,0 +1,43 @@
package main
type outputStruct struct {
BiosInfo biosInfo `json:"bios_info"`
OperatingSystem operatingSystem `json:"operating_system"`
InstalledPackages []installedPackage `json:"installed_packages"`
FsInformations []fsInfo `json:"fs_informations"`
LoadAverage loadAverage `json:"load_average"`
Cpus []cpu `json:"cpu"`
Ram ram `json:"ram"`
Uptime uptime `json:"uptime"`
TimeService timeService `json:"time_service"`
Firewalls firewalls `json:"firewalls"`
NetworkConfig networkConfig `json:"network_config"`
InternetRequired []internetResource `json:"internet_required"`
SecSetALSE secSetALSE `json:"security_settings_alse"`
SecSetUbuntu secSetUbuntu `json:"security_settings_ubuntu"`
SecSetAlma secSetAlma `json:"security_settings_alma"`
DockerPsA []dockerPs `json:"docker_ps_a"`
DockerStats []dockerStats `json:"docker_stats"`
DockerSupervisor []dockerSupervisor `json:"docker_supervisor"`
JournalctlEntries []journalctlEntry `json:"journalctl_entries"`
NftRuleset nftRuleset `json:"nft_rulest"`
RootHistory []rootHistoryCommand `json:"root_history_command"`
}
type platformStruct struct {
// IspSettings []ispSetting `json:"isp_settings"`
DciLocations []dciLocation `json:"dci_locations"` //+
DciLocationDockerComposes []dciLocationDockerCompose `json:"dci_location_dockercomposes"` //+
TaskManagerTask []taskManagerTask `json:"task_manager_task"` //+
HwByLocations []hwByLocation `json:"hw_by_locations"` //+
InstalledPlugin []installedPlugin `json:"installed_plugin"` //+
Users users `json:"users"` //+
Ldaps []ldap `json:"ldaps"` //+
Repos []repo `json:"repos"` //+
OsTemplates []osTemplate `json:"os_template"` //+
RealIP realIP `json:"real_ip"` //+
BackupTasks []backupTask `json:"backup_tasks"` //+
CopiedFiles copiedFiles `json:"copied_files"` //+
License license `json:"license"`
// ShowProcesslist []processlist `json:"show_processlist"`
}

View File

@@ -0,0 +1,133 @@
package main
// type ispSetting struct {
// // select name, value from isp_settings;
// Name string `json:"name"`
// Value string `json:"value"`
// }
type dciLocation struct {
// select id,status,status_info,name,ssh_address,ssh_port,ssh_user,setup_info,params_dhcp,params_redis,params_nginx,params_netflow,settings,is_main,proxy_params,userspace,patch from dci_1.dci_location;
Id int `json:"id"`
Status string `json:"status"`
StatusInfo string `json:"status_info"`
Name string `json:"name"`
SshAddress string `json:"ssh_address"`
SshPort int `json:"ssh_port"`
SshUser string `json:"ssh_user"`
SetupInfo string `json:"setup_info"`
ParamsDhcp string `json:"params_dhcp"`
ParamsRedis string `json:"params_redis"`
ParamsNginx string `json:"params_nginx"`
ParamsNetflow string `json:"params_netflow"`
Settings string `json:"settings"`
IsMain int `json:"is_main"`
ProxyParams string `json:"proxy_params"`
Userspace int `json:"userspace"`
Patch string `json:"patch"`
}
type dciLocationDockerCompose struct {
// select id,name,docker_compose from dci_1.dci_location;
Id int `json:"id"`
Name string `json:"name"`
DockerCompose string `json:"docker_compose"`
}
type taskManagerTask struct {
// select id,name,status,registration_time from auth.taskmgr_task where status != 'complete' and registration_time >= NOW() - INTERVAL 1 MONTH;
Id int `json:"id"`
Name string `json:"name"`
RegistrationTime string `json:"registration_time"`
RequestInfo string `json:"request_info"`
Output string `json:"output"`
Status string `json:"status"`
}
type hwByLocation struct {
// SELECT
// (SELECT COUNT(id) from dci_server WHERE location = 2) as server,
// (SELECT COUNT(id) from dci_switch WHERE location = 2) as switch,
// (SELECT count(id) from dci_pdu WHERE location = 2) as pdu,
// (SELECT COUNT(id) from dci_ups WHERE location = 2) as ups,
// (SELECT COUNT(id) from dci_server WHERE location = 2) +
// (SELECT COUNT(id) from dci_switch WHERE location = 2) +
// (SELECT count(id) from dci_pdu WHERE location = 2) +
// (SELECT COUNT(id) from dci_ups WHERE location = 2) as total_location_2;
Location int `json:"location"`
Server int `json:"server"`
Switch int `json:"switch"`
Pdu int `json:"pdu"`
Ups int `json:"ups"`
}
type installedPlugin struct {
// select name,status,version,current_version from ps_plugin;
Name string `json:"name"`
Status string `json:"string"`
Version string `json:"version"`
CurrentVersion string `json:"current_version"`
}
type users struct {
Count int `json:"count"`
}
type ldap struct {
Id int `json:"id"`
Name string `json:"name"`
LastSync string `json:"last_sync"`
}
type repo struct {
// SELECT id,name,url FROM dci_repo
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
}
type osTemplate struct {
// SELECT id,name,filename,macro,metainfo,directory_name,repository,size from dci_os_template where repo != 1 /* репозиторий ispsystem*/
Id int `json:"id"`
Name string `json:"name"`
Filename string `json:"filename"`
Macro string `json:"macro"`
Metainfo string `json:"metainfo"`
DirectoryName string `json:"directory_name"`
Repository int `json:"repository"`
Size int `json:"size"`
}
// type processlist struct {
// // SELECT * FROM information_schema.processlist WHERE COMMAND != 'Sleep' AND TIME > 0 ORDER BY TIME DESC;
// Id int `json:"id"`
// User string `json:"user"`
// Host string `json:"host"`
// Db string `json:"db"`
// Command string `json:"command"`
// Time int `json:"time"`
// State string `json:"state"`
// Info string `json:"info"`
// }
type realIP struct {
// SELECT name, value FROM auth.isp_settings WHERE name = "trusted_servers"
Name string `json:"name"`
Value string `json:"value"`
}
type backupTask struct {
// SELECT id,enabled,cron_expression,limit_count,limit_size_mib FROM backup_task
Id int `json:"id"`
Enabled int `json:"enabled"`
CronExpression string `json:"cron_expression"`
LimitCount int `json:"limit_count"`
LimitSizeMib int `json:"limit_size_mib"`
}
type copiedFiles struct {
FileNames []string `json:"file_names"`
Config string `json:"config"`
PlatformDockerCompose string `json:"platform_docker_compose"`
InstallLog string `json:"install_log"`
}

View File

@@ -0,0 +1,533 @@
package main
import (
"fmt"
"strconv"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func createLocationTab(platform platformStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск Локаций ...")
filteredEntries := platform.DciLocations
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewEntry(),
widget.NewEntry(),
widget.NewEntry(),
widget.NewEntry(),
widget.NewEntry(),
widget.NewEntry(),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
entryOne := box.Objects[0].(*widget.Entry)
entryTwo := box.Objects[1].(*widget.Entry)
entryThree := box.Objects[2].(*widget.Entry)
entryFour := box.Objects[3].(*widget.Entry)
entryFive := box.Objects[4].(*widget.Entry)
entrySix := box.Objects[5].(*widget.Entry)
entryOne.AlwaysShowValidationError = true
entryOne.SetText(fmt.Sprintf("%d Имя: %s Главная локация: %d Статус: %s (%s)", e.Id, e.Name, e.IsMain, e.Status, e.StatusInfo))
entryTwo.SetText(fmt.Sprintf("SSH: %s@%s:%d", e.SshUser, e.SshAddress, e.SshPort))
entryThree.SetText(fmt.Sprintf("%s Userspace: %d", e.SetupInfo, e.Userspace))
entryFour.SetText(fmt.Sprintf("Настройки: %s", e.Settings))
entryFive.SetText(fmt.Sprintf("Патч: %s", e.Patch))
entrySix.SetText(fmt.Sprintf("DHCP: %s Redis: %s Nginx: %s Netflow: %s Proxy: %s", e.ParamsDhcp, e.ParamsRedis, e.ParamsNginx, e.ParamsNetflow, e.ProxyParams))
entryOne.TextStyle.Bold = true
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = platform.DciLocations
} else {
filtered := []dciLocation{}
lText := strings.ToLower(text)
for _, elem := range platform.DciLocations {
if strings.Contains(strings.ToLower(elem.Name), lText) || strings.Contains(strings.ToLower(elem.SshAddress), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
locationsTab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return locationsTab
}
// func createLocationComposeTab(platform platformStruct) *fyne.Container {
// // строка поиска
// searchEntry := widget.NewEntry()
// searchEntry.SetPlaceHolder("Поиск ...")
// // переменная с которой будем работать дальше при фильтрации, с пустым условием фильтрации равна всему списку
// filteredEntries := platform.DciLocationDockerComposes
// // создаём список, котрый будет отображаться в табе
// list := widget.NewList(
// // состоит из трёх функций, которые постоянно дургаются, когда скролишь список
// // первая возвращает длину списка, что даёт возможность получить id элемента
// func() int {
// return len(filteredEntries)
// },
// // вторая работает шаблоном показывая из каких элементов список будет состоять
// func() fyne.CanvasObject {
// return container.NewVBox(
// widget.NewLabel("имя"),
// widget.NewMultiLineEntry(),
// )
// },
// // третья функция формирует собственно список с данными
// func(id widget.ListItemID, obj fyne.CanvasObject) {
// box := obj.(*fyne.Container)
// e := filteredEntries[id]
// nameLabel := box.Objects[0].(*widget.Label)
// composeEntry := box.Objects[1].(*widget.Entry)
// nameLabel.SetText(fmt.Sprintf("%d %s", e.Id, e.Name))
// nameLabel.TextStyle.Bold = true
// composeEntry.SetText(e.DockerCompose)
// },
// )
// // дальше формируем поисковую логику, которая вызывается при изменении строки
// searchEntry.OnChanged = func(text string) {
// if text == "" {
// filteredEntries = platform.DciLocationDockerComposes
// } else {
// filtered := []dciLocationDockerCompose{}
// lText := strings.ToLower(text)
// for _, elem := range platform.DciLocationDockerComposes {
// if strings.Contains(strings.ToLower(elem.Name), lText) || strings.Contains(strings.ToLower(elem.DockerCompose), lText) {
// filtered = append(filtered, elem)
// }
// }
// filteredEntries = filtered
// }
// list.Refresh()
// }
// locationsTab := container.NewBorder(
// container.NewVBox(
// searchEntry,
// widget.NewSeparator(),
// ), nil, nil, nil,
// container.NewScroll(list),
// )
// return locationsTab
// }
func createTaskManagerFailedTab(platform platformStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск задач ...")
filteredEntries := platform.TaskManagerTask
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("id name status"),
widget.NewLabel("time"),
widget.NewEntry(),
widget.NewLabel("output"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
idLabel := box.Objects[0].(*widget.Label)
nameLabel := box.Objects[1].(*widget.Label)
statusLabel := box.Objects[2].(*widget.Entry)
timeLabel := box.Objects[3].(*widget.Label)
idLabel.SetText(fmt.Sprintf("%d %s %s", e.Id, e.Name, e.Status))
idLabel.TextStyle.Bold = true
nameLabel.SetText(e.RegistrationTime)
statusLabel.SetText(e.RequestInfo)
timeLabel.SetText(e.Output)
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = platform.TaskManagerTask
} else {
filtered := []taskManagerTask{}
lText := strings.ToLower(text)
for _, elem := range platform.TaskManagerTask {
if strings.Contains(strings.ToLower(elem.Name), lText) || strings.Contains(strings.ToLower(elem.Status), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
taskFailedTab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return taskFailedTab
}
func createHwByLocationsTab(platform platformStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск по локациям ...")
filteredEntries := platform.HwByLocations
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("loc"),
widget.NewLabel("serv"),
widget.NewLabel("switch"),
widget.NewLabel("pdu"),
widget.NewLabel("ups"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
loc := box.Objects[0].(*widget.Label)
serv := box.Objects[1].(*widget.Label)
sw := box.Objects[2].(*widget.Label)
pdu := box.Objects[3].(*widget.Label)
ups := box.Objects[4].(*widget.Label)
loc.SetText(fmt.Sprintf("Локация: %d", e.Location))
loc.TextStyle.Bold = true
serv.SetText(fmt.Sprintf("Серверов: %d", e.Server))
sw.SetText(fmt.Sprintf("Коммутаторов: %d", e.Switch))
pdu.SetText(fmt.Sprintf("PDU: %d", e.Pdu))
ups.SetText(fmt.Sprintf("UPS: %d", e.Ups))
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = platform.HwByLocations
} else {
filtered := []hwByLocation{}
lText := strings.ToLower(text)
for _, elem := range platform.HwByLocations {
if strings.Contains(strconv.Itoa(elem.Location), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createInstalledPluginTab(platform platformStruct) *fyne.Container {
list := widget.NewList(
func() int {
return len(platform.InstalledPlugin)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("name"),
widget.NewLabel("status"),
widget.NewLabel("version"),
widget.NewLabel("Curver"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := platform.InstalledPlugin[id]
nameLabel := box.Objects[0].(*widget.Label)
statLabel := box.Objects[1].(*widget.Label)
verLabel := box.Objects[2].(*widget.Label)
curverLabel := box.Objects[3].(*widget.Label)
nameLabel.SetText(e.Name)
nameLabel.TextStyle.Bold = true
statLabel.SetText(e.Status)
verLabel.SetText(e.Version)
curverLabel.SetText(e.CurrentVersion)
},
)
list.Refresh()
tab := container.NewBorder(
container.NewVBox(
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createLdapTab(platform platformStruct) *fyne.Container {
list := widget.NewList(
func() int {
return len(platform.Ldaps)
},
// Id int `json:"id"`
// Name string `json:"name"`
// LastSync string `json:"last_sync"`
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("ldap"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := platform.Ldaps[id]
ldapLabel := box.Objects[0].(*widget.Label)
ldapLabel.SetText(fmt.Sprintf("%d %s Синхронизация: %s", e.Id, e.Name, e.LastSync))
},
)
list.Refresh()
tab := container.NewBorder(
container.NewVBox(
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createRepoTab(platform platformStruct) *fyne.Container {
list := widget.NewList(
func() int {
return len(platform.Repos)
},
// Id int `json:"id"`
// Name string `json:"name"`
// Url string `json:"url"`
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("id"),
widget.NewLabel("name"),
widget.NewLabel("url"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := platform.Repos[id]
idLabel := box.Objects[0].(*widget.Label)
nameLabel := box.Objects[1].(*widget.Label)
urlLabel := box.Objects[2].(*widget.Label)
idLabel.SetText(fmt.Sprintf("%d", e.Id))
nameLabel.SetText(e.Name)
urlLabel.SetText(e.Url)
},
)
list.Refresh()
tab := container.NewBorder(
container.NewVBox(
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createOsTemplateTab(platform platformStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := platform.OsTemplates
list := widget.NewList(
func() int {
return len(filteredEntries)
},
// Id int `json:"id"`
// Name string `json:"name"`
// Filename string `json:"filename"`
// Macro string `json:"macro"`
// Metainfo string `json:"metainfo"`
// DirectoryName string `json:"directory_name"`
// Repository int `json:"repository"`
// Size int `json:"size"`
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("id-name-filename-directoryname-size"),
widget.NewLabel("macro-metainfo-reposiztory"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
labelFirst := box.Objects[0].(*widget.Label)
labelSecond := box.Objects[1].(*widget.Label)
labelFirst.SetText(fmt.Sprintf("%d %s %s %s размер: %d", e.Id, e.Name, e.Filename, e.DirectoryName, e.Size))
labelFirst.Selectable = true
labelSecond.SetText(fmt.Sprintf("макросы: %s мета: %s репозиторий: %d", e.Macro, e.Metainfo, e.Repository))
labelSecond.Selectable = true
},
)
searchEntry.OnChanged = func(textRaw string) {
if textRaw == "" {
filteredEntries = platform.OsTemplates
} else {
filtered := []osTemplate{}
text := strings.ToLower(textRaw)
for _, elem := range platform.OsTemplates {
if strings.Contains(strings.ToLower(elem.Name), text) || strings.Contains(strings.ToLower(elem.Filename), text) || strings.Contains(strings.ToLower(elem.Metainfo), text) || strings.Contains(strings.ToLower(elem.DirectoryName), text) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
osTemplTab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return osTemplTab
}
func createBackupTasksTab(platform platformStruct) *fyne.Container {
// Id int `json:"id"`
// Enabled int `json:"enabled"`
// CronExpression string `json:"cron_expression"`
// LimitCount int `json:"limit_count"`
// LimitSizeMib int `json:"limit_size_mib"`
list := widget.NewList(
func() int {
return len(platform.BackupTasks)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("id"),
widget.NewLabel("enabled"),
widget.NewLabel("cron"),
widget.NewLabel("count"),
widget.NewLabel("size"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := platform.BackupTasks[id]
label0 := box.Objects[0].(*widget.Label)
label1 := box.Objects[1].(*widget.Label)
label2 := box.Objects[2].(*widget.Label)
label3 := box.Objects[3].(*widget.Label)
label4 := box.Objects[4].(*widget.Label)
label0.SetText(strconv.Itoa(e.Id))
label1.SetText(fmt.Sprintf("включено: %d", e.Enabled))
label2.SetText(e.CronExpression)
label3.SetText(fmt.Sprintf("количество копий: %d", e.LimitCount))
label4.SetText(fmt.Sprintf("размер: %d", e.LimitSizeMib))
},
)
list.Refresh()
tab := container.NewBorder(
container.NewVBox(
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createCopiedFileListTab(platform platformStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := platform.CopiedFiles.FileNames
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("fileName"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
fileName := box.Objects[0].(*widget.Label)
fileName.SetText(e)
fileName.Selectable = true
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = platform.CopiedFiles.FileNames
} else {
var filtered []string
lText := strings.ToLower(text)
for _, elem := range platform.CopiedFiles.FileNames {
if strings.Contains(strings.ToLower(elem), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createLicenseTab(platform platformStruct) *fyne.Container {
licCheck := widget.NewMultiLineEntry()
licCheck.SetText(platform.License.LicenseCheck)
licInfo := widget.NewMultiLineEntry()
licInfo.SetText(platform.License.LicenseInfo)
licInfo.MultiLine = true
licInfo.SetMinRowsVisible(20)
tab := container.NewBorder(
container.NewVBox(
licCheck,
licInfo,
), nil, nil, nil,
)
return tab
}

View File

@@ -0,0 +1,6 @@
package main
type license struct {
LicenseCheck string `json:"license_check"`
LicenseInfo string `json:"license_info"`
}

View File

@@ -0,0 +1,264 @@
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
)
// Очистка вкладок server
func (a *App) clearServerTabs() {
// Удаляем все вкладки, кроме первой (если она нужна)
items := a.serverTab.Items
if len(items) > 0 {
// Удаляем все вкладки после первой (если первая - это какая-то базовая)
// Или удаляем все, если нужно полностью очистить
for i := len(items) - 1; i >= 0; i-- {
a.serverTab.RemoveIndex(i)
}
}
}
// Очистка вкладок platform
func (a *App) clearPlatformTabs() {
items := a.platformTab.Items
if len(items) > 0 {
for i := len(items) - 1; i >= 0; i-- {
a.platformTab.RemoveIndex(i)
}
}
}
// Обновление UI после загрузки
func (a *App) updateUIAfterUpload(uiFunc string) {
// Обновляем статус
a.statusLabel.SetText("✅ Файл загружен")
a.statusLabel.TextStyle = fyne.TextStyle{Bold: true, Italic: true}
a.statusLabel.Refresh()
// Обновляем информацию о файле
fileInfoText := fmt.Sprintf("📄 %s | 📦 %s | 🕒 %s",
a.fileName,
formatFileSize(a.fileSize),
time.Now().Format("15:04:05"))
a.fileInfo.SetText(fileInfoText)
if uiFunc == "server" {
a.updateServer()
}
if uiFunc == "platform" {
a.updatePlatform()
}
}
// Обновление содержимого server
func (a *App) updateServer() {
a.clearServerTabs()
var server outputStruct
err := json.Unmarshal(a.serverContent, &server)
if err != nil {
log.Printf("Unmarshall server: %s", err)
}
a.serverTab.Append(container.NewTabItem("БИОС", widget.NewLabel(server.BiosInfo.Name)))
if server.OperatingSystem.AstraLicense == "" || server.OperatingSystem.AstraVersion == "" {
a.serverTab.Append(container.NewTabItem("ОС", widget.NewLabel(server.OperatingSystem.OsRelease)))
} else {
a.serverTab.Append(container.NewTabItem("ОС", widget.NewLabel(fmt.Sprintf("Версия: %s, Версия ALSE: %s, Уровень защиты ALSE: %s", server.OperatingSystem.OsRelease, server.OperatingSystem.AstraLicense, server.OperatingSystem.AstraVersion))))
}
installedPkgsTab := createInstalledPkgsTab(server)
a.serverTab.Append(container.NewTabItem("Пакеты", installedPkgsTab))
fsInfoTab := createFsInfoTab(server)
a.serverTab.Append(container.NewTabItem("ФС", fsInfoTab))
a.serverTab.Append(container.NewTabItem("LA", widget.NewLabel(fmt.Sprintf("Средняя загрузка: 15м %s\n, \t5м %s\n, \t1м %s\n, Процессы %s", server.LoadAverage.FifteenMin, server.LoadAverage.FiveMin, server.LoadAverage.OneMin, server.LoadAverage.Processes))))
cpuTab := createCpuTab(server)
a.serverTab.Append(container.NewTabItem("ЦП", cpuTab))
a.serverTab.Append(container.NewTabItem("ОЗУ", widget.NewLabel(fmt.Sprintf("Доступно: %s\nСвободно: %s\nВсего: %s", server.Ram.MemAvailable, server.Ram.MemFree, server.Ram.MemTotal))))
a.serverTab.Append(container.NewTabItem("Uptime", widget.NewLabel(server.Uptime.WorkSeconds)))
a.serverTab.Append(container.NewTabItem("Фаерволы", createFirewallTab(server)))
a.serverTab.Append(container.NewTabItem("Время", widget.NewLabel(fmt.Sprintf("Служба времени\t\t\t%s, \nВремя синхронизировано\t%s", server.TimeService.TimeService, server.TimeService.TimeSync))))
a.serverTab.Append(container.NewTabItem("Сеть", createNetworkTab(server)))
// a.serverTab.Append(container.NewTabItem("Интеренет", widget.NewLabel(fmt.Sprintf("docker-registry.ispsystem.com :: %d\ndownload.ispsystem.com :: %d\nlicense6.ispsystem.com :: %d\nmetricreport.ispsystem.net :: %d\ndownload.docker.com :: %d", server.InternetRequired.IspRegistry, server.InternetRequired.IspDownload, server.InternetRequired.IspLicense, server.InternetRequired.IspMetric, server.InternetRequired.DockerDownload))))
a.serverTab.Append(container.NewTabItem("Интернет", createInternetRequiredTab(server)))
a.serverTab.Append(container.NewTabItem("Сесюрити", createSecSetTab(server)))
a.serverTab.Append(container.NewTabItem("Docker", createDockerTab(server)))
a.serverTab.Append(container.NewTabItem("Jrnlctl", createJournalTab(server)))
a.serverTab.Append(container.NewTabItem("root history", createRootHistoryTab(server)))
}
func (a *App) updatePlatform() {
a.clearPlatformTabs()
var platform platformStruct
err := json.Unmarshal(a.platformContent, &platform)
if err != nil {
log.Printf("Unmarshall platform: %s", err)
}
a.platformTab.Append(container.NewTabItem("Лицензия", createLicenseTab(platform)))
a.platformTab.Append(container.NewTabItem("Локации", createLocationTab(platform)))
// a.platformTab.Append(container.NewTabItem("Compose Локация", createLocationComposeTab(platform)))
a.platformTab.Append(container.NewTabItem("Имена файлов", createCopiedFileListTab(platform)))
a.platformTab.Append(container.NewTabItem("Taskmgr", createTaskManagerFailedTab(platform)))
a.platformTab.Append(container.NewTabItem("Оборудование", createHwByLocationsTab(platform)))
a.platformTab.Append(container.NewTabItem("Плагины", createInstalledPluginTab(platform)))
a.platformTab.Append(container.NewTabItem("Пользователи", widget.NewLabel(fmt.Sprintf("Количество пользователей: %d", platform.Users.Count))))
a.platformTab.Append(container.NewTabItem("LDAP", createLdapTab(platform)))
a.platformTab.Append(container.NewTabItem("Репозитории", createRepoTab(platform)))
a.platformTab.Append(container.NewTabItem("Шаблоны ОС", createOsTemplateTab(platform)))
a.platformTab.Append(container.NewTabItem("Real ip", widget.NewLabel(fmt.Sprintf("%s : %s", platform.RealIP.Name, platform.RealIP.Value))))
a.platformTab.Append(container.NewTabItem("Бэкап", createBackupTasksTab(platform)))
}
func (a *App) onUploadServer() {
a.statusLabel.SetText("Выбор файла...")
// Фильтр json файлов
jsonFilter := storage.NewExtensionFileFilter([]string{".json"})
// Создаем диалог выбора файла
fileDialog := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
a.showError("Ошибка", fmt.Sprintf("Не удалось открыть диалог: %v", err))
return
}
if reader == nil {
a.statusLabel.SetText("Отменено")
return
}
defer reader.Close()
// Получаем информацию о файле
fileURI := reader.URI()
a.filePath = fileURI.Path()
a.fileName = fileURI.Name()
// Читаем файл
content, err := io.ReadAll(reader)
if err != nil {
a.showError("Ошибка чтения", fmt.Sprintf("Не удалось прочитать файл: %v", err))
a.progressBar.Hide()
return
}
// Сохраняем данные
a.serverContent = content
a.fileSize = int64(len(content))
// Завершаем прогресс
a.progressBar.SetValue(1.0)
time.Sleep(300 * time.Millisecond)
a.progressBar.Hide()
// Обновляем UI
a.updateUIAfterUpload("server")
// Показываем уведомление
a.showSuccess("Файл загружен",
fmt.Sprintf("Файл '%s' успешно загружен", a.fileName))
}, a.window)
fileDialog.SetFilter(jsonFilter)
// Альтернативный способ установки начальной директории
// Используем домашнюю директорию пользователя
homeDir, err := os.UserHomeDir()
if err == nil {
// Пытаемся использовать Downloads
downloadsPath := filepath.Join(homeDir, "Downloads")
if _, err := os.Stat(downloadsPath); err == nil {
// Создаем URI из пути
uri := storage.NewFileURI(downloadsPath)
listableURI, err := storage.ListerForURI(uri)
if err == nil {
fileDialog.SetLocation(listableURI)
}
}
}
// Показываем диалог
fileDialog.Show()
}
func (a *App) onUploadPlatform() {
a.statusLabel.SetText("Выбор файла...")
// Фильтр json файлов
jsonFilter := storage.NewExtensionFileFilter([]string{".json"})
// Создаем диалог выбора файла
fileDialog := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
a.showError("Ошибка", fmt.Sprintf("Не удалось открыть диалог: %v", err))
return
}
if reader == nil {
a.statusLabel.SetText("Отменено")
return
}
defer reader.Close()
// Получаем информацию о файле
fileURI := reader.URI()
a.filePath = fileURI.Path()
a.fileName = fileURI.Name()
// Читаем файл
content, err := io.ReadAll(reader)
if err != nil {
a.showError("Ошибка чтения", fmt.Sprintf("Не удалось прочитать файл: %v", err))
a.progressBar.Hide()
return
}
// Сохраняем данные
a.platformContent = content
a.fileSize = int64(len(content))
// Завершаем прогресс
a.progressBar.SetValue(1.0)
time.Sleep(300 * time.Millisecond)
a.progressBar.Hide()
// Обновляем UI
a.updateUIAfterUpload("platform")
// Показываем уведомление
a.showSuccess("Файл загружен",
fmt.Sprintf("Файл '%s' успешно загружен", a.fileName))
}, a.window)
fileDialog.SetFilter(jsonFilter)
// Альтернативный способ установки начальной директории
// Используем домашнюю директорию пользователя
homeDir, err := os.UserHomeDir()
if err == nil {
// Пытаемся использовать Downloads
downloadsPath := filepath.Join(homeDir, "Downloads")
if _, err := os.Stat(downloadsPath); err == nil {
// Создаем URI из пути
uri := storage.NewFileURI(downloadsPath)
listableURI, err := storage.ListerForURI(uri)
if err == nil {
fileDialog.SetLocation(listableURI)
}
}
}
// Показываем диалог
fileDialog.Show()
}

View File

@@ -0,0 +1,248 @@
package main
import (
"fmt"
"strconv"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func createDockerTab(server outputStruct) *container.AppTabs {
dockerTabs := container.NewAppTabs()
dockerTabs.Append(container.NewTabItem("docker ps", createDockerPsTab(server)))
dockerTabs.Append(container.NewTabItem("docker stats", createDockerStats(server)))
dockerTabs.Append(container.NewTabItem("supervisor status", createDockerSupervisorTab(server)))
return dockerTabs
}
// type dockerStats struct {
// BlockIO string `json:"BlockIO"`
// CPUPerc string `json:"CPUPerc"`
// Container string `json:"Container"`
// ID string `json:"ID"`
// MemPerc string `json:"MemPerc"`
// MemUsage string `json:"MemUsage"`
// Name string `json:"Name"`
// NetIO string `json:"NetIO"`
// PIDs string `json:"PIDs"`
// }
func shortId(id string) string {
if len(id) > 12 {
return id[:12]
}
return id
}
func parsePercent(percent string) float64 {
var val float64
fmt.Sscanf(percent, "%f%%", &val)
return val
}
func createDockerStats(server outputStruct) *fyne.Container {
// Заголовки таблицы
headers := []string{"CONTAINER", "NAME", "CPU %", "MEM %", "MEM USAGE", "NET I/O", "BLOCK I/O", "PIDs"}
// Данные
stats := server.DockerStats
// Создаю таблицу
table := widget.NewTable(
func() (rows int, cols int) {
return len(stats) + 1, len(headers)
},
func() fyne.CanvasObject {
label := widget.NewLabel("Template")
label.Selectable = true
label.Alignment = fyne.TextAlignCenter
return label
},
func(id widget.TableCellID, obj fyne.CanvasObject) {
label := obj.(*widget.Label)
// Заголовки
if id.Row == 0 {
label.SetText(headers[id.Col])
label.TextStyle.Bold = true
label.Alignment = fyne.TextAlignCenter
}
// Данные
row := id.Row - 1
// if row < len(stats) {
if len(stats) > 0 && row >= 0 && row < len(stats) {
switch id.Col {
case 0:
// label.SetText(shortId(stats[row].Container))
label.SetText(stats[row].Container)
case 1:
label.SetText(stats[row].Name)
case 2:
label.SetText(stats[row].CPUPerc)
case 3:
label.SetText(stats[row].MemPerc)
if memPercent := parsePercent(stats[row].MemPerc); memPercent > 50 {
label.Importance = widget.HighImportance
}
case 4:
label.SetText(stats[row].MemUsage)
case 5:
label.SetText(stats[row].NetIO)
case 6:
label.SetText(stats[row].BlockIO)
case 7:
label.SetText(stats[row].PIDs)
}
}
},
)
//Размер колонок
table.SetColumnWidth(0, 100)
table.SetColumnWidth(1, 150)
table.SetColumnWidth(2, 70)
table.SetColumnWidth(3, 70)
table.SetColumnWidth(4, 140)
table.SetColumnWidth(5, 140)
table.SetColumnWidth(6, 140)
table.SetColumnWidth(7, 50)
table.Refresh()
tab := container.NewBorder(
widget.NewSeparator(),
nil, nil, nil,
container.NewScroll(table),
)
return tab
}
func createDockerPsTab(server outputStruct) *fyne.Container {
searchDockerEntry := widget.NewEntry()
searchDockerEntry.SetPlaceHolder("Поиск контейнеров ...")
filteredDockers := server.DockerPsA
// type dockerPs struct {
// Id string `json:"id"`
// Image string `json:"image"`
// Names []string `json:"names"`
// Created int64 `json:"running_for"`
// State string `json:"state"`
// Status string `json:"status"`
// }
dockerList := widget.NewList(
func() int {
return len(filteredDockers)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("id"),
widget.NewLabel("image"),
widget.NewLabel("names"),
widget.NewLabel("created"),
widget.NewLabel("state"),
widget.NewLabel("status"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
dock := filteredDockers[id]
idLabel := hbox.Objects[0].(*widget.Label)
imageLabel := hbox.Objects[1].(*widget.Label)
namesLabel := hbox.Objects[2].(*widget.Label)
createdLabel := hbox.Objects[3].(*widget.Label)
stateLabel := hbox.Objects[4].(*widget.Label)
statusLabel := hbox.Objects[5].(*widget.Label)
idLabel.SetText(dock.Id)
imageLabel.SetText(dock.Image)
namesLabel.SetText(fmt.Sprintf("%s", dock.Names))
createdLabel.SetText(strconv.Itoa(int(dock.Created)))
stateLabel.SetText(dock.State)
statusLabel.SetText(dock.Status)
},
)
searchDockerEntry.OnChanged = func(text string) {
if text == "" {
filteredDockers = server.DockerPsA
} else {
filtered := []dockerPs{}
lowerText := strings.ToLower(text)
for _, d := range server.DockerPsA {
if strings.Contains(strings.ToLower(d.Image), lowerText) || strings.Contains(strings.ToLower(d.State), lowerText) || strings.Contains(strings.ToLower(fmt.Sprintf("%s", d.Names)), lowerText) || strings.Contains(strings.ToLower(d.Status), lowerText) {
filtered = append(filtered, d)
}
}
filteredDockers = filtered
}
dockerList.Refresh()
}
dockerTab := container.NewBorder(
container.NewVBox(
searchDockerEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(dockerList),
)
return dockerTab
}
func createDockerSupervisorTab(server outputStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filtered := server.DockerSupervisor
list := widget.NewList(
func() int {
return len(filtered)
},
func() fyne.CanvasObject {
entry := widget.NewMultiLineEntry()
entry.SetMinRowsVisible(10)
entry.Wrapping = fyne.TextWrapWord
return container.NewVBox(
widget.NewLabel("container_name"),
entry,
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filtered[id]
containerName := box.Objects[0].(*widget.Label)
containerName.SetText(e.Name)
containerName.TextStyle.Bold = true
containerName.Selectable = true
supervisorStatus := box.Objects[1].(*widget.Entry)
supervisorStatus.SetText(e.SupStatus)
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filtered = server.DockerSupervisor
} else {
var f []dockerSupervisor
lText := strings.ToLower(text)
for _, elem := range server.DockerSupervisor {
if strings.Contains(strings.ToLower(elem.Name), lText) {
f = append(f, elem)
}
}
filtered = f
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}

View File

@@ -0,0 +1,113 @@
// добавить генерацию файлов с правилами в архив, добавить номера строк, что бы хоть как то ориентироваться и в жопу это
package main
import (
"fmt"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
// widget.NewLabel(fmt.Sprintf("UFW\t%s\nNftables\t%s\nFirewalld\t%s\nIptables\t%s", server.Firewalls.Ufw, server.Firewalls.Nftables, server.Firewalls.Firewalld, server.Firewalls.Iptables))
func createFirewallTab(server outputStruct) *container.AppTabs {
firewallTabs := container.NewAppTabs()
firewallTabs.Append(container.NewTabItem("список фаерволов", createFirewallListTab(server)))
firewallTabs.Append(container.NewTabItem("nft list ruleset", createNftListRuleset(server)))
return firewallTabs
}
func createFirewallListTab(server outputStruct) *fyne.Container {
firewalls := ""
if server.Firewalls.Firewalld != "" {
firewalls = firewalls + "firewalld"
}
if server.Firewalls.Nftables != "" {
firewalls = firewalls + ", nftables"
}
if server.Firewalls.Ufw != "" {
firewalls = firewalls + ", ufw"
}
tab := container.NewBorder(
container.NewVBox(
widget.NewSeparator(),
widget.NewLabel(fmt.Sprintf("Активные службы фаерволов: %s", firewalls)),
), nil, nil, nil,
)
return tab
}
func createNftListRuleset(server outputStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := server.NftRuleset.Lines
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("fileName"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
fileName := box.Objects[0].(*widget.Label)
fileName.SetText(e)
fileName.Selectable = true
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = server.NftRuleset.Lines
} else {
var filtered []string
lText := strings.ToLower(text)
for _, elem := range server.NftRuleset.Lines {
if strings.Contains(strings.ToLower(elem), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
// searchEntry.OnChanged = func(text string) {
// if text == "" {
// // Если поиск пустой - сбрасываем выделение
// list.UnselectAll()
// return
// }
// lText := strings.ToLower(text)
// // Ищем первое вхождение
// for index, elem := range server.NftRuleset.Lines {
// if strings.Contains(strings.ToLower(elem), lText) {
// // Выделяем найденный элемент
// list.Select(index)
// // Прокручиваем к нему
// list.ScrollTo(index)
// return
// }
// }
// // Если ничего не найдено - сбрасываем выделение
// list.UnselectAll()
// }
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}

View File

@@ -0,0 +1,8 @@
package main
// я расчитываю в дальнейшем вернуться к этому. Json структура nftables непростая, поэтому я или разгадаю сам как её анмаршалить, или сопру из одной из двух библиотек на гитхабе. Или вообще их заиспользую.
// пока что просто построчно выведу правила nft list ruleset
type nftRuleset struct {
Lines []string `json:"lines"`
}

View File

@@ -0,0 +1,162 @@
package main
type biosInfo struct {
Name string `json:"name"`
}
type operatingSystem struct {
OsRelease string `json:"os_name"`
AstraVersion string `json:"astra_version"`
AstraLicense string `json:"astra_license"`
}
type installedPackage struct {
Name string `json:"name"`
Version string `json:"version"`
}
type fsInfo struct {
MountPointDevice string `json:"mount_point_devices"`
MountPointOsPath string `json:"mount_point_os_path"`
MountPointSize uint64 `json:"mount_point_size"`
MountPointFree uint64 `json:"mount_point_free"`
}
type loadAverage struct {
OneMin string `json:"la_onemin"`
FiveMin string `json:"la_fivemin"`
FifteenMin string `json:"la_fifteenmin"`
Processes string `json:"la_processes"`
}
type cpu struct {
VendorId string `json:"vendor_id"`
CpuFamily string `json:"cpu_family"`
Model string `json:"model"`
ModelName string `json:"model_name"`
Stepping string `json:"stepping"`
Microcode string `json:"microcode"`
CpuMhz string `json:"cpu_mhz"`
CacheSize string `json:"cache_size"`
PhysicalId string `json:"phusical_id"`
CpuCores string `json:"cpu_cores"`
Flags string `json:"flags"`
Bugs string `json:"bugs"`
Bogomips string `json:"bogomips"`
TlbSize string `json:"tlb_size"`
ClflushSize string `json:"clflush_size"`
CacheAlignment string `json:"cache_alignment"`
AddressSize string `json:"address_size"`
PowerManagement string `json:"power_management"`
}
type ram struct {
MemTotal string `json:"mem_total"`
MemFree string `json:"mem_free"`
MemAvailable string `json:"mem_available"`
}
type uptime struct {
WorkSeconds string `json:"work_seconds"`
}
type timeService struct {
TimeService string `json:"time_service"`
TimeSync string `json:"time_sync"`
}
type journalctlEntry struct {
Timestamp string `json:"__REALTIME_TIMESTAMP"`
CommandLine string `json:"_CMDLINE"`
CommandName string `json:"_COMM"`
SyslogIdentifier string `json:"SYSLOG_IDENTIFIER"`
Message string `json:"MESSAGE"`
}
type firewalls struct {
Ufw string `json:"ufw"`
Nftables string `json:"nftables"`
Iptables string `json:"iptables"`
Firewalld string `json:"firewalld"`
}
type netInterface struct {
Device string `json:"device"`
Flags string `json:"flags"`
HwAddr string `json:"hardware_address"`
IpAddr []string `json:"ip_addr"`
Index string `json:"index"`
MTU string `json:"mtu"`
}
type networkConfig struct {
NetInterfaces []netInterface `json:"net_interfaces"`
NetworkManager string `json:"networkmanager"`
NetworkManagerConn []string `json:"networkmanager_conn"`
Networking string `json:"networking"`
EtcHosts []string `json:"etc_hosts"`
}
type internetResource struct {
Name string `json:"name"`
Http string `json:"http"`
Https string `json:"https"`
IpList string `json:"ip_list"`
}
type secSetALSE struct {
Apparmor string `json:"apparmor"`
Selinux string `json:"selinux"`
Parsec string `json:"parsec"`
Mic string `json:"astra_mic"`
Mac string `json:"astra_mac"`
Digsig string `json:"digsig"`
Sudo string `json:"sudo"`
Nochmodx string `json:"nochmodx"`
Interpret string `json:"interpret"`
SudoersAstraAdmin string `json:"sudoers_astraadmin"`
SudoersSudo string `json:"sudoers_sudo"`
AstraAdmin string `json:"astra-admin"`
}
type secSetUbuntu struct {
Apparmor string `json:"apparmor"`
}
type secSetAlma struct {
Selinux string `json:"selinux"`
}
type dockerPs struct {
Id string `json:"id"`
Image string `json:"image"`
Names []string `json:"names"`
Created int64 `json:"running_for"`
State string `json:"state"`
Status string `json:"status"`
// Health string `json:"health"`
}
type dockerStats struct {
BlockIO string `json:"BlockIO"`
CPUPerc string `json:"CPUPerc"`
Container string `json:"Container"`
ID string `json:"ID"`
MemPerc string `json:"MemPerc"`
MemUsage string `json:"MemUsage"`
Name string `json:"Name"`
NetIO string `json:"NetIO"`
PIDs string `json:"PIDs"`
}
type dockerSupervisor struct {
Id string `json:"id"`
Name string `json:"name"`
SupStatus string `json:"sup_status"`
}
type rootHistoryCommand struct {
Id int `json:"id"`
ExecutionTime string `json:"excution_time"`
Command string `json:"command"`
}

View File

@@ -0,0 +1,489 @@
package main
import (
"fmt"
"log"
"strconv"
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func createInstalledPkgsTab(server outputStruct) *fyne.Container {
searchPkgEntry := widget.NewEntry()
searchPkgEntry.SetPlaceHolder("Поиск пакетов ...")
filteredPackages := server.InstalledPackages
installedPackagesList := widget.NewList(
func() int {
return len(filteredPackages)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("Пакет"),
widget.NewLabel("Версия"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
pkg := filteredPackages[id]
nameLabel := hbox.Objects[0].(*widget.Label)
versionLabel := hbox.Objects[1].(*widget.Label)
nameLabel.SetText(pkg.Name)
versionLabel.SetText(pkg.Version)
versionLabel.TextStyle = fyne.TextStyle{Monospace: true}
},
)
searchPkgEntry.OnChanged = func(text string) {
if text == "" {
filteredPackages = server.InstalledPackages
} else {
filtered := []installedPackage{}
lowerText := strings.ToLower(text)
for _, pkg := range server.InstalledPackages {
if strings.Contains(strings.ToLower(pkg.Name), lowerText) ||
strings.Contains(strings.ToLower(pkg.Version), lowerText) {
filtered = append(filtered, pkg)
}
}
filteredPackages = filtered
}
installedPackagesList.Refresh()
}
installedPkgsTab := container.NewBorder(
container.NewVBox(
searchPkgEntry,
widget.NewSeparator(),
),
nil, nil, nil,
container.NewScroll(installedPackagesList),
)
return installedPkgsTab
}
func createFsInfoTab(server outputStruct) *fyne.Container {
searchFsInfoEntry := widget.NewEntry()
searchFsInfoEntry.SetPlaceHolder("Поиск монтирований...")
filteredFs := server.FsInformations
fsInfoList := widget.NewList(
func() int {
return len(filteredFs)
},
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewLabel("Устройство"),
widget.NewLabel("Путь монтирования"),
widget.NewLabel("Размер"),
widget.NewLabel("Свободно"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
hbox := obj.(*fyne.Container)
fs := filteredFs[id]
deviceLabel := hbox.Objects[0].(*widget.Label)
pathLabel := hbox.Objects[1].(*widget.Label)
sizeLabel := hbox.Objects[2].(*widget.Label)
freeLabel := hbox.Objects[3].(*widget.Label)
deviceLabel.SetText(fs.MountPointDevice)
pathLabel.SetText(fs.MountPointOsPath)
sizeLabel.SetText(strconv.Itoa(int(fs.MountPointSize)))
freeLabel.SetText(strconv.Itoa(int(fs.MountPointFree)))
freeLabel.TextStyle.Bold = true
},
)
searchFsInfoEntry.OnChanged = func(text string) {
if text == "" {
filteredFs = server.FsInformations
} else {
filtered := []fsInfo{}
lowerText := strings.ToLower(text)
for _, fs := range server.FsInformations {
if strings.Contains(strings.ToLower(fs.MountPointDevice), lowerText) ||
strings.Contains(strings.ToLower(fs.MountPointOsPath), lowerText) {
filtered = append(filtered, fs)
}
}
filteredFs = filtered
}
fsInfoList.Refresh()
}
fsInfoTab := container.NewBorder(
container.NewVBox(
searchFsInfoEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(fsInfoList),
)
return fsInfoTab
}
// func createCpuTab(server outputStruct) *fyne.Container {
// searchCpuEntry := widget.NewEntry()
// searchCpuEntry.SetPlaceHolder("ЦПУ...")
// filteredCpu := server.Cpu.Model
// cpuList := widget.NewList(
// func() int {
// return len(filteredCpu)
// },
// func() fyne.CanvasObject {
// return container.NewHBox(
// widget.NewLabel("Модель"),
// )
// },
// func(id widget.ListItemID, obj fyne.CanvasObject) {
// hbox := obj.(*fyne.Container)
// cpu := filteredCpu[id]
// modelLabel := hbox.Objects[0].(*widget.Label)
// modelLabel.SetText(cpu)
// },
// )
// searchCpuEntry.OnChanged = func(text string) {
// if text == "" {
// filteredCpu = server.Cpu.Model
// } else {
// filtered := cpu{}
// lowerText := strings.ToLower(text)
// for _, c := range server.Cpu.Model {
// if strings.Contains(strings.ToLower(c), lowerText) {
// filtered.Model = append(filtered.Model, c)
// }
// }
// filteredCpu = filtered.Model
// }
// cpuList.Refresh()
// }
// cpuTab := container.NewBorder(
// container.NewVBox(
// searchCpuEntry,
// widget.NewSeparator(),
// ), nil, nil, nil,
// container.NewScroll(cpuList),
// )
// return cpuTab
// }
func createCpuTab(server outputStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := server.Cpus
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("VendorID,CpuFamily,Model,ModelName"),
widget.NewLabel("Stepping,Microcode,CpuMhz,CacheSize"),
widget.NewLabel("PhysicalId,CpuCores,Fpu,FpuException,Wp"),
widget.NewLabel("Flags"),
widget.NewLabel("Bugs"),
widget.NewLabel("Bogomips,TlbSize,ClflushSize"),
widget.NewLabel("CacheAlignment,AddressSize,PowerManagement"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
label0 := box.Objects[0].(*widget.Label)
label1 := box.Objects[1].(*widget.Label)
label2 := box.Objects[2].(*widget.Label)
label3 := box.Objects[3].(*widget.Label)
label4 := box.Objects[4].(*widget.Label)
label5 := box.Objects[5].(*widget.Label)
label6 := box.Objects[6].(*widget.Label)
label0.SetText(fmt.Sprintf("VendorId: %s, CpuFamily: %s, Model: %s, ModelName: %s", e.VendorId, e.CpuFamily, e.Model, e.ModelName))
label1.SetText(fmt.Sprintf("Stepping: %s, Microcode: %s, CpuMhz: %s, CacheSize: %s", e.Stepping, e.Microcode, e.CpuMhz, e.CacheSize))
label2.SetText(fmt.Sprintf("PhysicalId: %s, CpuCores: %s", e.PhysicalId, e.CpuCores))
label3.SetText(fmt.Sprintf("Flags: %s", e.Flags))
label4.SetText(fmt.Sprintf("Bugs: %s", e.Bugs))
label5.SetText(fmt.Sprintf("Bogomips: %s, TlbSize: %s, ClflushSize: %s", e.Bogomips, e.TlbSize, e.ClflushSize))
label6.SetText(fmt.Sprintf("CacheAlignment: %s, AddressSize: %s, PowerManagement: %s", e.CacheAlignment, e.AddressSize, e.PowerManagement))
},
)
searchEntry.OnChanged = func(textRaw string) {
if textRaw == "" {
filteredEntries = server.Cpus
} else {
filtered := []cpu{}
text := strings.ToLower(textRaw)
for _, elem := range server.Cpus {
if strings.Contains(strings.ToLower(elem.Flags), text) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createInternetRequiredTab(server outputStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := server.InternetRequired
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
// widget.NewCard("Title", "Subtitle", widget.NewLabel("content")),
widget.NewLabel("name"),
widget.NewLabel("ip_list"),
widget.NewLabel("http+https"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
label0 := box.Objects[0].(*widget.Label)
label1 := box.Objects[1].(*widget.Label)
label2 := box.Objects[2].(*widget.Label)
label0.SetText(e.Name)
label0.TextStyle.Bold = true
label1.SetText(e.IpList)
label1.Selectable = true
label2.SetText(fmt.Sprintf("HTTP: %s HTTPS: %s", e.Http, e.Https))
label2.Selectable = true
// card := box.Objects[0].(*widget.Card)
// card.SetTitle(e.Name)
// card.SetSubTitle(e.IpList)
// card.SetContent(widget.NewLabel(fmt.Sprintf("HTTP: %s HTTPS: %s", e.Http, e.Https)))
},
)
searchEntry.OnChanged = func(textRaw string) {
if textRaw == "" {
filteredEntries = server.InternetRequired
} else {
filtered := []internetResource{}
text := strings.ToLower(textRaw)
for _, elem := range server.InternetRequired {
if strings.Contains(strings.ToLower(elem.Name), text) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}
func createSecSetTab(server outputStruct) *widget.Label {
if strings.Contains(server.OperatingSystem.OsRelease, "Alma") {
return widget.NewLabel(fmt.Sprintf("Selinux :: %s", server.SecSetAlma.Selinux))
} else if strings.Contains(server.OperatingSystem.OsRelease, "Ubuntu") {
return widget.NewLabel(fmt.Sprintf("Apparmor :: %s", server.SecSetUbuntu.Apparmor))
} else {
return widget.NewLabel(fmt.Sprintf("Apparmor\t%s\nAstraAdmin\t%s\nDigsig\t%s\nИнтерпретаторы\t%s\nmac\t%s\nmic\t%s\nnochmodx\t%s\nparsec\t%s\nselinux\t%s\nsudo control\t%s\nsudoers astra admin\t%s\nsudoers sudo\t%s", server.SecSetALSE.Apparmor, server.SecSetALSE.AstraAdmin, server.SecSetALSE.Digsig, server.SecSetALSE.Interpret, server.SecSetALSE.Mac, server.SecSetALSE.Mic, server.SecSetALSE.Nochmodx, server.SecSetALSE.Parsec, server.SecSetALSE.Selinux, server.SecSetALSE.Sudo, server.SecSetALSE.SudoersAstraAdmin, server.SecSetALSE.SudoersSudo))
}
}
// Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne
func createJournalTab(server outputStruct) *fyne.Container {
searchJrlEntry := widget.NewEntry()
searchJrlEntry.SetPlaceHolder("Поиск событий ...")
filteredEvents := server.JournalctlEntries
// Создаем список
journalctlEntriesList := widget.NewList(
func() int {
return len(filteredEvents)
},
func() fyne.CanvasObject {
// Возвращаем кастомный виджет
return NewJournalListItem(journalctlEntry{})
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
// Обновляем виджет
widget := obj.(*JournalListItem)
widget.Update(filteredEvents[id])
},
)
searchJrlEntry.OnChanged = func(text string) {
if text == "" {
filteredEvents = server.JournalctlEntries
} else {
filtered := []journalctlEntry{}
lowerText := strings.ToLower(text)
for _, ev := range server.JournalctlEntries {
if strings.Contains(strings.ToLower(ev.CommandLine), lowerText) ||
strings.Contains(strings.ToLower(ev.CommandName), lowerText) ||
strings.Contains(strings.ToLower(ev.SyslogIdentifier), lowerText) ||
strings.Contains(strings.ToLower(ev.Message), lowerText) {
filtered = append(filtered, ev)
}
}
filteredEvents = filtered
}
journalctlEntriesList.Refresh()
}
journalctlEntriesTab := container.NewBorder(
container.NewVBox(
searchJrlEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(journalctlEntriesList),
)
return journalctlEntriesTab
}
// func createJournalTab(server outputStruct) *fyne.Container {
// searchJrlEntry := widget.NewEntry()
// searchJrlEntry.SetPlaceHolder("Поиск событий ...")
// filteredEvents := server.JournalctlEntries
// journalctlEntriesList := widget.NewList(
// func() int {
// return len(filteredEvents)
// },
// func() fyne.CanvasObject {
// label := widget.NewLabel("entry")
// label.Wrapping = fyne.TextWrapBreak
// // label.Resize(fyne.Size{Height: 50})
// return container.NewVBox(
// label,
// // widget.NewSeparator(),
// )
// },
// func(id widget.ListItemID, obj fyne.CanvasObject) {
// vbox := obj.(*fyne.Container)
// event := filteredEvents[id]
// eventEntry := vbox.Objects[0].(*widget.Label)
// eventEntry.SetText(fmt.Sprintf("%s :: %s :: %s :: %s :: %s", event.Timestamp, event.CommandLine, event.CommandName, event.SyslogIdentifier, event.Message))
// eventEntry.Selectable = true
// eventEntry.Wrapping = fyne.TextWrapBreak
// var doubleHeight fyne.Size
// doubleHeight.Height = 72.0
// if eventEntry.Size().Height >= 36.0 {
// eventEntry.Resize(doubleHeight)
// }
// // eventEntry.Truncation = fyne.TextTruncateClip
// },
// )
// searchJrlEntry.OnChanged = func(text string) {
// if text == "" {
// filteredEvents = server.JournalctlEntries
// } else {
// filtered := []journalctlEntry{}
// lowerText := strings.ToLower(text)
// for _, ev := range server.JournalctlEntries {
// if strings.Contains(strings.ToLower(ev.CommandLine), lowerText) || strings.Contains(strings.ToLower(ev.CommandName), lowerText) || strings.Contains(strings.ToLower(ev.SyslogIdentifier), lowerText) || strings.Contains(strings.ToLower(ev.Message), lowerText) {
// filtered = append(filtered, ev)
// }
// }
// filteredEvents = filtered
// }
// journalctlEntriesList.Refresh()
// }
// journalctlEntriesTab := container.NewBorder(
// container.NewVBox(
// searchJrlEntry,
// widget.NewSeparator(),
// ), nil, nil, nil,
// container.NewScroll(journalctlEntriesList),
// )
// return journalctlEntriesTab
// }
func createRootHistoryTab(server outputStruct) *fyne.Container {
searchEntry := widget.NewEntry()
searchEntry.SetPlaceHolder("Поиск ...")
filteredEntries := server.RootHistory
list := widget.NewList(
func() int {
return len(filteredEntries)
},
func() fyne.CanvasObject {
return container.NewVBox(
widget.NewLabel("template"),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
box := obj.(*fyne.Container)
e := filteredEntries[id]
fileName := box.Objects[0].(*widget.Label)
timeExec := ""
if len(e.ExecutionTime) != 0 {
timeInt64, err := strconv.ParseInt(e.ExecutionTime, 10, 64)
if err != nil {
log.Println(err)
timeExec = e.ExecutionTime
} else {
timeExec = time.Unix(timeInt64, 0).String()
}
}
fileName.SetText(fmt.Sprintf("%d %s %s", e.Id, timeExec, e.Command))
fileName.Selectable = true
},
)
searchEntry.OnChanged = func(text string) {
if text == "" {
filteredEntries = server.RootHistory
} else {
var filtered []rootHistoryCommand
lText := strings.ToLower(text)
for _, elem := range server.RootHistory {
if strings.Contains(strings.ToLower(elem.Command), lText) {
filtered = append(filtered, elem)
}
}
filteredEntries = filtered
}
list.Refresh()
}
tab := container.NewBorder(
container.NewVBox(
searchEntry,
widget.NewSeparator(),
), nil, nil, nil,
container.NewScroll(list),
)
return tab
}

View File

@@ -0,0 +1 @@
# 2602-3