feat(gui): redesign kanholec GUI with modern interface and WiX v7 installer
golangci-lint / lint (push) Failing after 4s
golangci-lint / lint (push) Failing after 4s
- Complete GUI overhaul with Fyne framework * Modern dark theme with indigo/pink accent colors * 4-step setup wizard (Welcome → Server → Auth → Finish) * Dashboard with proxy list, real-time logs, and config viewer * Windows Service manager (install/start/stop/uninstall) * System tray integration with quick controls - WiX v7 MSI installer * Auto-registers as Windows Service (manual start) * Feature tree UI for optional components * Desktop shortcut and PATH options * Build script: packaging/windows/build-msi.ps1 - Build system updates * Added kanholec-windows-gui target (CGO required) * Added kanholec-windows-msi target * Separate main_gui.go entry point for GUI builds GUI binary: bin/kanholec-windows-amd64.exe (31.4 MB) MSI installer: bin/kanholec-0.69.0-amd64.msi (12.1 MB)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
//go:build kanholec_gui
|
||||
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type ServiceManager struct {
|
||||
binaryPath string
|
||||
serviceName string
|
||||
}
|
||||
|
||||
func NewServiceManager() *ServiceManager {
|
||||
exe, _ := os.Executable()
|
||||
return &ServiceManager{
|
||||
binaryPath: exe,
|
||||
serviceName: "kanholec",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceManager) IsInstalled() bool {
|
||||
if runtime.GOOS != "windows" {
|
||||
return false
|
||||
}
|
||||
cmd := exec.Command("sc", "query", s.serviceName)
|
||||
err := cmd.Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *ServiceManager) Install(configPath string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return fmt.Errorf("service installation is only supported on Windows")
|
||||
}
|
||||
|
||||
if s.IsInstalled() {
|
||||
return fmt.Errorf("service %q is already installed", s.serviceName)
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"create", s.serviceName,
|
||||
"binPath=", fmt.Sprintf(`"%s" -c "%s"`, s.binaryPath, configPath),
|
||||
"DisplayName=", "kanholec - kanhole Client",
|
||||
"start=", "auto",
|
||||
"obj=", "LocalSystem",
|
||||
}
|
||||
|
||||
cmd := exec.Command("sc", args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install service: %s: %w", string(out), err)
|
||||
}
|
||||
|
||||
descArgs := []string{"description", s.serviceName, "kanhole reverse proxy client service"}
|
||||
cmd = exec.Command("sc", descArgs...)
|
||||
cmd.Run()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServiceManager) Uninstall() error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return fmt.Errorf("service management is only supported on Windows")
|
||||
}
|
||||
|
||||
if !s.IsInstalled() {
|
||||
return fmt.Errorf("service %q is not installed", s.serviceName)
|
||||
}
|
||||
|
||||
s.Stop()
|
||||
|
||||
cmd := exec.Command("sc", "delete", s.serviceName)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to uninstall service: %s: %w", string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServiceManager) Start() error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return fmt.Errorf("service management is only supported on Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("sc", "start", s.serviceName)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start service: %s: %w", string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServiceManager) Stop() error {
|
||||
if runtime.GOOS != "windows" {
|
||||
return fmt.Errorf("service management is only supported on Windows")
|
||||
}
|
||||
|
||||
cmd := exec.Command("sc", "stop", s.serviceName)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop service: %s: %w", string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ServiceManager) IsRunning() bool {
|
||||
if runtime.GOOS != "windows" {
|
||||
return false
|
||||
}
|
||||
cmd := exec.Command("sc", "query", s.serviceName)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return contains(string(out), "RUNNING")
|
||||
}
|
||||
|
||||
func (s *ServiceManager) GetConfigPath() string {
|
||||
programData := os.Getenv("ProgramData")
|
||||
if programData == "" {
|
||||
programData = `C:\ProgramData`
|
||||
}
|
||||
return filepath.Join(programData, "kanholec", "kanholec.toml")
|
||||
}
|
||||
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) >= len(substr) && searchString(s, substr)
|
||||
}
|
||||
|
||||
func searchString(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user