Initial commit
This commit is contained in:
commit
88a1f0f50f
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*build*
|
||||||
|
*\dict
|
||||||
|
.idea
|
10985
cmd/installer/defaultSettings.json
Normal file
10985
cmd/installer/defaultSettings.json
Normal file
File diff suppressed because it is too large
Load Diff
24
cmd/installer/main.go
Normal file
24
cmd/installer/main.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fyne.io/fyne/v2/app"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/installer"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed defaultSettings.json
|
||||||
|
var defaultSettings string
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create application
|
||||||
|
myApp := app.New()
|
||||||
|
|
||||||
|
// Create installer
|
||||||
|
femaInstaller := installer.NewInstaller(defaultSettings)
|
||||||
|
|
||||||
|
// Create and show main window
|
||||||
|
mainWindow := ui.NewMainWindow(myApp, femaInstaller.Install)
|
||||||
|
mainWindow.ShowAndRun()
|
||||||
|
}
|
33
cmd/updater/main.go
Normal file
33
cmd/updater/main.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fyne.io/fyne/v2/app"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/ui"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/updater"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed build
|
||||||
|
var binaryData []byte
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Load configuration
|
||||||
|
configPath := updater.GetConfigFilePath()
|
||||||
|
cfg, err := config.LoadUpdaterConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Ошибка загрузки конфигурации: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create application
|
||||||
|
myApp := app.New()
|
||||||
|
|
||||||
|
// Create updater
|
||||||
|
femaUpdater := updater.NewUpdater(cfg, binaryData)
|
||||||
|
|
||||||
|
// Create and show updater window
|
||||||
|
updaterWindow := ui.NewUpdaterWindow(myApp, cfg, femaUpdater.Update)
|
||||||
|
updaterWindow.ShowAndRun()
|
||||||
|
}
|
6
cmd/updater/updater_config.json
Normal file
6
cmd/updater/updater_config.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ip": "192.168.111.111",
|
||||||
|
"port": "22",
|
||||||
|
"login": "root",
|
||||||
|
"password": "orangepi"
|
||||||
|
}
|
45
go.mod
Normal file
45
go.mod
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module gitea.unprism.ru/KRBL/FemaInstaller
|
||||||
|
|
||||||
|
go 1.24
|
||||||
|
|
||||||
|
require (
|
||||||
|
fyne.io/fyne/v2 v2.6.0
|
||||||
|
github.com/pkg/sftp v1.13.9
|
||||||
|
golang.org/x/crypto v0.37.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
fyne.io/systray v1.11.0 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fredbi/uri v1.1.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/fyne-io/gl-js v0.1.0 // indirect
|
||||||
|
github.com/fyne-io/glfw-js v0.2.0 // indirect
|
||||||
|
github.com/fyne-io/image v0.1.1 // indirect
|
||||||
|
github.com/fyne-io/oksvg v0.1.0 // indirect
|
||||||
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
|
||||||
|
github.com/go-text/render v0.2.0 // indirect
|
||||||
|
github.com/go-text/typesetting v0.3.0 // 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.1 // indirect
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 // indirect
|
||||||
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||||
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rymdport/portal v0.4.1 // 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.10.0 // indirect
|
||||||
|
github.com/yuin/goldmark v1.7.10 // indirect
|
||||||
|
golang.org/x/image v0.26.0 // indirect
|
||||||
|
golang.org/x/net v0.39.0 // indirect
|
||||||
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
golang.org/x/text v0.24.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
158
go.sum
Normal file
158
go.sum
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
fyne.io/fyne/v2 v2.6.0 h1:Rywo9yKYN4qvNuvkRuLF+zxhJYWbIFM+m4N4KV4p1pQ=
|
||||||
|
fyne.io/fyne/v2 v2.6.0/go.mod h1:YZt7SksjvrSNJCwbWFV32WON3mE1Sr7L41D29qMZ/lU=
|
||||||
|
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
||||||
|
fyne.io/systray v1.11.0/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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
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/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||||
|
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||||
|
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
|
||||||
|
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
|
||||||
|
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.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
|
||||||
|
github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
||||||
|
github.com/fyne-io/glfw-js v0.2.0 h1:8GUZtN2aCoTPNqgRDxK5+kn9OURINhBEBc7M4O1KrmM=
|
||||||
|
github.com/fyne-io/glfw-js v0.2.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/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
|
||||||
|
github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
|
||||||
|
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-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
|
||||||
|
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.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
|
||||||
|
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
|
||||||
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||||
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||||
|
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/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
||||||
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||||
|
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.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
||||||
|
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 h1:vFdvrlsVU+p/KFBWTq0lTG4fvWvG88sawGlCzM+RUEU=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||||
|
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/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
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.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||||
|
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||||
|
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
|
||||||
|
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
|
||||||
|
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.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
||||||
|
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
|
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-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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
|
||||||
|
github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
|
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||||
|
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
|
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
|
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||||
|
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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=
|
96
internal/installer/installer.go
Normal file
96
internal/installer/installer.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package installer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/ssh"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/config"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/fileutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Installer handles the installation process
|
||||||
|
type Installer struct {
|
||||||
|
DefaultSettings string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInstaller creates a new installer with the provided default settings
|
||||||
|
func NewInstaller(defaultSettings string) *Installer {
|
||||||
|
return &Installer{
|
||||||
|
DefaultSettings: defaultSettings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install performs the installation process
|
||||||
|
func (i *Installer) Install(sshConfig *config.SSHConfig) error {
|
||||||
|
// Validate serial number
|
||||||
|
if len([]rune(sshConfig.Serial)) != 16 {
|
||||||
|
return fmt.Errorf("serial number must be 16 characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update settings with user configuration
|
||||||
|
updatedSettings, err := config.UpdateSettingsJSON(i.DefaultSettings, sshConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save updated settings to file
|
||||||
|
err = config.SaveSettingsJSON(updatedSettings, "settings.json")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to save settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SSH client configuration
|
||||||
|
clientConfig := ssh.NewClientConfig(
|
||||||
|
sshConfig.IP,
|
||||||
|
sshConfig.Port,
|
||||||
|
sshConfig.Login,
|
||||||
|
sshConfig.Password,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Connect to SSH server
|
||||||
|
sshClient, err := ssh.CreateSSHClient(clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("SSH connection failed: %w", err)
|
||||||
|
}
|
||||||
|
defer sshClient.Close()
|
||||||
|
|
||||||
|
// Create SFTP client
|
||||||
|
sftpClient, err := ssh.CreateSFTPClient(clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("SFTP connection failed: %w", err)
|
||||||
|
}
|
||||||
|
defer sftpClient.Close()
|
||||||
|
|
||||||
|
// Upload archive
|
||||||
|
remotePath := "/root/dict.tar"
|
||||||
|
if err = fileutils.UploadFile(sftpClient, sshConfig.ArchivePath, remotePath); err != nil {
|
||||||
|
return fmt.Errorf("file upload failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload settings
|
||||||
|
settingsPath := "/root/settings.json"
|
||||||
|
if err = fileutils.UploadFile(sftpClient, "settings.json", settingsPath); err != nil {
|
||||||
|
return fmt.Errorf("settings upload failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute installation commands
|
||||||
|
commands := []string{
|
||||||
|
"tar -xf /root/dict.tar -C /root/",
|
||||||
|
"mkdir -p /root/fema/storage",
|
||||||
|
"mv -f ~/settings.json /root/fema/storage",
|
||||||
|
"chmod +x /root/dict/*",
|
||||||
|
fmt.Sprintf("sudo /root/dict/install.sh -s %s", sshConfig.Serial),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range commands {
|
||||||
|
if err := ssh.ExecuteCommand(sshClient, cmd); err != nil {
|
||||||
|
return fmt.Errorf("command execution failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up temporary files
|
||||||
|
os.Remove("settings.json")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
89
internal/ssh/client.go
Normal file
89
internal/ssh/client.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/sftp"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientConfig holds the configuration for SSH connections
|
||||||
|
type ClientConfig struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientConfig creates a new SSH client configuration
|
||||||
|
func NewClientConfig(host, port, username, password string) *ClientConfig {
|
||||||
|
return &ClientConfig{
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSSHClient creates and returns an SSH client using the provided configuration
|
||||||
|
func CreateSSHClient(config *ClientConfig) (*ssh.Client, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, fmt.Errorf("SSH config cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
sshConfig := &ssh.ClientConfig{
|
||||||
|
User: config.Username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(config.Password),
|
||||||
|
},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
Timeout: config.Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := ssh.Dial("tcp", config.Host+":"+config.Port, sshConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to SSH server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSFTPClient creates and returns an SFTP client using the provided configuration
|
||||||
|
func CreateSFTPClient(config *ClientConfig) (*sftp.Client, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, fmt.Errorf("SFTP config cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SSH client
|
||||||
|
sshClient, err := CreateSSHClient(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SFTP client
|
||||||
|
sftpClient, err := sftp.NewClient(sshClient)
|
||||||
|
if err != nil {
|
||||||
|
sshClient.Close() // Close the SSH client in case of failure
|
||||||
|
return nil, fmt.Errorf("failed to create SFTP client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftpClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteCommand executes a command on the remote server
|
||||||
|
func ExecuteCommand(client *ssh.Client, command string) error {
|
||||||
|
session, err := client.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("session creation failed: %w", err)
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
if err := session.Run(command); err != nil {
|
||||||
|
return fmt.Errorf("command '%s' failed: %w", command, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
49
internal/ui/file_selector.go
Normal file
49
internal/ui/file_selector.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomFileSelector displays a custom file selection dialog
|
||||||
|
func CustomFileSelector(window fyne.Window, setText func(string)) {
|
||||||
|
// Get list of files in current directory
|
||||||
|
files, _ := filepath.Glob("*")
|
||||||
|
|
||||||
|
// Create list widget with files
|
||||||
|
fileList := widget.NewList(
|
||||||
|
func() int {
|
||||||
|
return len(files)
|
||||||
|
},
|
||||||
|
func() fyne.CanvasObject {
|
||||||
|
return widget.NewLabel("File")
|
||||||
|
},
|
||||||
|
func(id widget.ListItemID, obj fyne.CanvasObject) {
|
||||||
|
obj.(*widget.Label).SetText(files[id])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set selection handler
|
||||||
|
fileList.OnSelected = func(id widget.ListItemID) {
|
||||||
|
setText(files[id]) // Set the selected path in the text field
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create dialog title
|
||||||
|
title := widget.NewLabel("Files in current directory:")
|
||||||
|
title.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
|
// Create layout with title and file list
|
||||||
|
content := container.NewBorder(
|
||||||
|
title, nil, nil, nil, // Title at the top
|
||||||
|
container.NewMax(fileList), // List takes maximum space
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create and show dialog
|
||||||
|
fileDialog := dialog.NewCustom("Select a file", "Close", content, window)
|
||||||
|
fileDialog.Resize(fyne.NewSize(400, 500)) // Set dialog size
|
||||||
|
fileDialog.Show()
|
||||||
|
}
|
120
internal/ui/main_window.go
Normal file
120
internal/ui/main_window.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MainWindow represents the main application window
|
||||||
|
type MainWindow struct {
|
||||||
|
Window fyne.Window
|
||||||
|
IPEntry *widget.Entry
|
||||||
|
PortEntry *widget.Entry
|
||||||
|
LoginEntry *widget.Entry
|
||||||
|
PasswordEntry *widget.Entry
|
||||||
|
SerialEntry *widget.Entry
|
||||||
|
TailNumberEntry *widget.Entry
|
||||||
|
DefaultHostEntry *widget.Entry
|
||||||
|
ArchivePathEntry *widget.Entry
|
||||||
|
StatusLabel *widget.Label
|
||||||
|
InstallButton *widget.Button
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMainWindow creates a new main window for the application
|
||||||
|
func NewMainWindow(app fyne.App, installHandler func(*config.SSHConfig) error) *MainWindow {
|
||||||
|
window := app.NewWindow("Fema Installer")
|
||||||
|
|
||||||
|
// Create form fields
|
||||||
|
mainWindow := &MainWindow{
|
||||||
|
Window: window,
|
||||||
|
IPEntry: widget.NewEntry(),
|
||||||
|
PortEntry: widget.NewEntry(),
|
||||||
|
LoginEntry: widget.NewEntry(),
|
||||||
|
PasswordEntry: widget.NewPasswordEntry(),
|
||||||
|
SerialEntry: widget.NewEntry(),
|
||||||
|
TailNumberEntry: widget.NewEntry(),
|
||||||
|
DefaultHostEntry: widget.NewEntry(),
|
||||||
|
ArchivePathEntry: widget.NewEntry(),
|
||||||
|
StatusLabel: widget.NewLabel(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable archive path entry (will be set by file selector)
|
||||||
|
mainWindow.ArchivePathEntry.Disable()
|
||||||
|
|
||||||
|
// Create archive selection button
|
||||||
|
selectArchiveBtn := widget.NewButton("Select Archive", func() {
|
||||||
|
CustomFileSelector(window, func(path string) {
|
||||||
|
mainWindow.ArchivePathEntry.SetText(path)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create install button
|
||||||
|
mainWindow.InstallButton = widget.NewButton("Install", func() {
|
||||||
|
config := &config.SSHConfig{
|
||||||
|
IP: mainWindow.IPEntry.Text,
|
||||||
|
Port: mainWindow.PortEntry.Text,
|
||||||
|
Login: mainWindow.LoginEntry.Text,
|
||||||
|
Password: mainWindow.PasswordEntry.Text,
|
||||||
|
Serial: mainWindow.SerialEntry.Text,
|
||||||
|
TailNumber: mainWindow.TailNumberEntry.Text,
|
||||||
|
DefaultHost: mainWindow.DefaultHostEntry.Text,
|
||||||
|
ArchivePath: mainWindow.ArchivePathEntry.Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.StatusLabel.SetText("Starting installation...")
|
||||||
|
go func() {
|
||||||
|
// Validate serial number
|
||||||
|
if len([]rune(config.Serial)) != 16 {
|
||||||
|
mainWindow.StatusLabel.SetText("Serial number must be 16 characters")
|
||||||
|
dialog.ShowInformation("Error", "Serial number must be 16 characters", window)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform installation
|
||||||
|
err := installHandler(config)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(err, window)
|
||||||
|
mainWindow.StatusLabel.SetText("Installation failed")
|
||||||
|
} else {
|
||||||
|
mainWindow.StatusLabel.SetText("Installation completed successfully!")
|
||||||
|
dialog.ShowInformation("Success", "Installation completed successfully!", window)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create form layout
|
||||||
|
form := container.NewVBox(
|
||||||
|
widget.NewForm(
|
||||||
|
widget.NewFormItem("IP", mainWindow.IPEntry),
|
||||||
|
widget.NewFormItem("Port", mainWindow.PortEntry),
|
||||||
|
widget.NewFormItem("Login", mainWindow.LoginEntry),
|
||||||
|
widget.NewFormItem("Password", mainWindow.PasswordEntry),
|
||||||
|
widget.NewFormItem("Serial Number", mainWindow.SerialEntry),
|
||||||
|
widget.NewFormItem("Tail Number", mainWindow.TailNumberEntry),
|
||||||
|
widget.NewFormItem("Default Server", mainWindow.DefaultHostEntry),
|
||||||
|
widget.NewFormItem("Archive", container.NewHBox(mainWindow.ArchivePathEntry, selectArchiveBtn)),
|
||||||
|
),
|
||||||
|
mainWindow.InstallButton,
|
||||||
|
mainWindow.StatusLabel,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set window content and size
|
||||||
|
window.SetContent(form)
|
||||||
|
window.Resize(fyne.NewSize(400, 300))
|
||||||
|
|
||||||
|
return mainWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show displays the main window
|
||||||
|
func (w *MainWindow) Show() {
|
||||||
|
w.Window.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowAndRun displays the main window and starts the application
|
||||||
|
func (w *MainWindow) ShowAndRun() {
|
||||||
|
w.Window.ShowAndRun()
|
||||||
|
}
|
81
internal/ui/updater_window.go
Normal file
81
internal/ui/updater_window.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdaterWindow represents the main window for the updater application
|
||||||
|
type UpdaterWindow struct {
|
||||||
|
Window fyne.Window
|
||||||
|
ConfigDisplay *widget.Label
|
||||||
|
UpdateButton *widget.Button
|
||||||
|
StatusLabel *widget.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUpdaterWindow creates a new window for the updater application
|
||||||
|
func NewUpdaterWindow(app fyne.App, config *config.UpdaterConfig, updateHandler func() error) *UpdaterWindow {
|
||||||
|
window := app.NewWindow("Обновление ПО Фема")
|
||||||
|
|
||||||
|
// Create updater window
|
||||||
|
updaterWindow := &UpdaterWindow{
|
||||||
|
Window: window,
|
||||||
|
ConfigDisplay: widget.NewLabel(config.String()),
|
||||||
|
StatusLabel: widget.NewLabel(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create update button
|
||||||
|
updaterWindow.UpdateButton = widget.NewButton("Обновить ПО", func() {
|
||||||
|
updaterWindow.StatusLabel.SetText("Начало обновления...")
|
||||||
|
updaterWindow.UpdateButton.Disable()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := updateHandler()
|
||||||
|
if err != nil {
|
||||||
|
updaterWindow.StatusLabel.SetText("Ошибка обновления: " + err.Error())
|
||||||
|
} else {
|
||||||
|
updaterWindow.StatusLabel.SetText("Обновление успешно завершено!")
|
||||||
|
}
|
||||||
|
updaterWindow.UpdateButton.Enable()
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create title
|
||||||
|
title := widget.NewLabel("Программа обновления ПО Фема")
|
||||||
|
title.Alignment = fyne.TextAlignCenter
|
||||||
|
title.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
|
||||||
|
// Create config section title
|
||||||
|
configTitle := widget.NewLabel("Текущая конфигурация:")
|
||||||
|
configTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
|
|
||||||
|
// Create layout
|
||||||
|
content := container.NewVBox(
|
||||||
|
title,
|
||||||
|
widget.NewSeparator(),
|
||||||
|
configTitle,
|
||||||
|
updaterWindow.ConfigDisplay,
|
||||||
|
widget.NewSeparator(),
|
||||||
|
updaterWindow.UpdateButton,
|
||||||
|
updaterWindow.StatusLabel,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set window content and size
|
||||||
|
window.SetContent(content)
|
||||||
|
window.Resize(fyne.NewSize(400, 300))
|
||||||
|
|
||||||
|
return updaterWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show displays the updater window
|
||||||
|
func (w *UpdaterWindow) Show() {
|
||||||
|
w.Window.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowAndRun displays the updater window and starts the application
|
||||||
|
func (w *UpdaterWindow) ShowAndRun() {
|
||||||
|
w.Window.ShowAndRun()
|
||||||
|
}
|
89
internal/updater/updater.go
Normal file
89
internal/updater/updater.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package updater
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/internal/ssh"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/config"
|
||||||
|
"gitea.unprism.ru/KRBL/FemaInstaller/pkg/fileutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Updater handles the software update process
|
||||||
|
type Updater struct {
|
||||||
|
Config *config.UpdaterConfig
|
||||||
|
BinaryData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUpdater creates a new updater with the provided configuration and binary data
|
||||||
|
func NewUpdater(config *config.UpdaterConfig, binaryData []byte) *Updater {
|
||||||
|
return &Updater{
|
||||||
|
Config: config,
|
||||||
|
BinaryData: binaryData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update performs the software update process
|
||||||
|
func (u *Updater) Update() error {
|
||||||
|
// Save binary to temporary file
|
||||||
|
tempFile := "update_binary"
|
||||||
|
if err := os.WriteFile(tempFile, u.BinaryData, 0644); err != nil {
|
||||||
|
return fmt.Errorf("не удалось сохранить временный файл: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tempFile) // Clean up temporary file
|
||||||
|
|
||||||
|
// Create SSH client configuration
|
||||||
|
clientConfig := ssh.NewClientConfig(
|
||||||
|
u.Config.IP,
|
||||||
|
u.Config.Port,
|
||||||
|
u.Config.Login,
|
||||||
|
u.Config.Password,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Connect to SSH server
|
||||||
|
sshClient, err := ssh.CreateSSHClient(clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ошибка подключения SSH: %w", err)
|
||||||
|
}
|
||||||
|
defer sshClient.Close()
|
||||||
|
|
||||||
|
// Create SFTP client
|
||||||
|
sftpClient, err := ssh.CreateSFTPClient(clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ошибка подключения SFTP: %w", err)
|
||||||
|
}
|
||||||
|
defer sftpClient.Close()
|
||||||
|
|
||||||
|
// Upload binary
|
||||||
|
remotePath := "/root/fema/build.new"
|
||||||
|
if err = fileutils.UploadFile(sftpClient, tempFile, remotePath); err != nil {
|
||||||
|
return fmt.Errorf("ошибка загрузки файла: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute update commands
|
||||||
|
commands := []string{
|
||||||
|
"mv -f /root/fema/build.new /root/fema/build",
|
||||||
|
"chmod +x /root/fema/build",
|
||||||
|
"systemctl restart fema.service",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range commands {
|
||||||
|
if err := ssh.ExecuteCommand(sshClient, cmd); err != nil {
|
||||||
|
return fmt.Errorf("ошибка выполнения команды '%s': %w", cmd, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigFilePath returns the path to the configuration file
|
||||||
|
func GetConfigFilePath() string {
|
||||||
|
// Get executable directory
|
||||||
|
execDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
if err != nil {
|
||||||
|
execDir = "."
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(execDir, "updater_config.json")
|
||||||
|
}
|
61
pkg/config/config.go
Normal file
61
pkg/config/config.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents the application configuration
|
||||||
|
type Config struct {
|
||||||
|
DefaultSettings string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSHConfig holds SSH connection and installation parameters
|
||||||
|
type SSHConfig struct {
|
||||||
|
IP string
|
||||||
|
Port string
|
||||||
|
Login string
|
||||||
|
Password string
|
||||||
|
Serial string
|
||||||
|
TailNumber string
|
||||||
|
DefaultHost string
|
||||||
|
ArchivePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSSHConfig creates a new SSH configuration with the provided parameters
|
||||||
|
func NewSSHConfig(ip, port, login, password, serial, tailNumber, defaultHost, archivePath string) *SSHConfig {
|
||||||
|
return &SSHConfig{
|
||||||
|
IP: ip,
|
||||||
|
Port: port,
|
||||||
|
Login: login,
|
||||||
|
Password: password,
|
||||||
|
Serial: serial,
|
||||||
|
TailNumber: tailNumber,
|
||||||
|
DefaultHost: defaultHost,
|
||||||
|
ArchivePath: archivePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSettingsJSON updates the default settings JSON with user-provided values
|
||||||
|
func UpdateSettingsJSON(defaultSettings string, config *SSHConfig) (string, error) {
|
||||||
|
// Replace registration number
|
||||||
|
updated := strings.Replace(defaultSettings, `"REG_NUMBER" : "",`, fmt.Sprintf(`"REG_NUMBER" : "%s",`, config.TailNumber), 1)
|
||||||
|
|
||||||
|
// Split the default host into domain and port
|
||||||
|
hostParts := strings.Split(config.DefaultHost, ":")
|
||||||
|
if len(hostParts) != 2 {
|
||||||
|
return "", fmt.Errorf("invalid default host format, expected domain:port")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace domain and port
|
||||||
|
updated = strings.Replace(updated, `"DOMAIN" : "",`, fmt.Sprintf(`"DOMAIN" : "%s",`, hostParts[0]), 1)
|
||||||
|
updated = strings.Replace(updated, `"PORT" : 0`, fmt.Sprintf(`"PORT" : %s`, hostParts[1]), 1)
|
||||||
|
|
||||||
|
return updated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSettingsJSON saves the updated settings JSON to a file
|
||||||
|
func SaveSettingsJSON(content string, filePath string) error {
|
||||||
|
return os.WriteFile(filePath, []byte(content), 0644)
|
||||||
|
}
|
73
pkg/config/updater_config.go
Normal file
73
pkg/config/updater_config.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdaterConfig holds the configuration for the updater application
|
||||||
|
type UpdaterConfig struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Port string `json:"port"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUpdaterConfig creates a new updater configuration with default values
|
||||||
|
func NewUpdaterConfig() *UpdaterConfig {
|
||||||
|
return &UpdaterConfig{
|
||||||
|
IP: "127.0.0.1",
|
||||||
|
Port: "22",
|
||||||
|
Login: "root",
|
||||||
|
Password: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadUpdaterConfig loads the updater configuration from a file
|
||||||
|
func LoadUpdaterConfig(filePath string) (*UpdaterConfig, error) {
|
||||||
|
// Check if file exists
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
// Create default config if file doesn't exist
|
||||||
|
config := NewUpdaterConfig()
|
||||||
|
if err := SaveUpdaterConfig(config, filePath); err != nil {
|
||||||
|
return nil, fmt.Errorf("не удалось создать конфигурационный файл: %w", err)
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file
|
||||||
|
data, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("не удалось прочитать конфигурационный файл: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON
|
||||||
|
var config UpdaterConfig
|
||||||
|
if err := json.Unmarshal(data, &config); err != nil {
|
||||||
|
return nil, fmt.Errorf("не удалось разобрать конфигурационный файл: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveUpdaterConfig saves the updater configuration to a file
|
||||||
|
func SaveUpdaterConfig(config *UpdaterConfig, filePath string) error {
|
||||||
|
// Marshal JSON
|
||||||
|
data, err := json.MarshalIndent(config, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("не удалось сериализовать конфигурацию: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write file
|
||||||
|
if err := os.WriteFile(filePath, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("не удалось записать конфигурационный файл: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the updater configuration
|
||||||
|
func (c *UpdaterConfig) String() string {
|
||||||
|
return fmt.Sprintf("IP: %s\nПорт: %s\nЛогин: %s\nПароль: %s", c.IP, c.Port, c.Login, c.Password)
|
||||||
|
}
|
33
pkg/fileutils/fileutils.go
Normal file
33
pkg/fileutils/fileutils.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package fileutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/sftp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadFile uploads a local file to a remote server using SFTP
|
||||||
|
func UploadFile(client *sftp.Client, localPath, remotePath string) error {
|
||||||
|
// Open the local file
|
||||||
|
localFile, err := os.Open(localPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open local file: %w", err)
|
||||||
|
}
|
||||||
|
defer localFile.Close()
|
||||||
|
|
||||||
|
// Create the remote file
|
||||||
|
remoteFile, err := client.Create(remotePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create remote file: %w", err)
|
||||||
|
}
|
||||||
|
defer remoteFile.Close()
|
||||||
|
|
||||||
|
// Copy from the local file to the remote file
|
||||||
|
if _, err = io.Copy(remoteFile, localFile); err != nil {
|
||||||
|
return fmt.Errorf("failed to upload file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user