f4a88f4b2c
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)
142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
//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
|
|
}
|