feat: migrate to golang

This commit is contained in:
2026-07-19 20:01:38 +08:00
parent 302d4e6bb5
commit a2630df9e0
69 changed files with 4750 additions and 3369 deletions

122
internal/service/service.go Normal file
View File

@ -0,0 +1,122 @@
// Package service provides a cross-platform interface for installing,
// starting, stopping, and uninstalling mihomo as a system service —
// systemd on Linux, launchd on macOS, and a native Windows Service (via
// golang.org/x/sys/windows/svc) on Windows.
package service
import (
"fmt"
"os"
"strings"
)
// Config describes a service to install. Mirrors service_manager.py's
// pydantic ServiceConfig model.
type Config struct {
Name string
ExecutablePath string
Description string
Args string
}
func validateConfig(cfg Config) error {
name := strings.TrimSpace(cfg.Name)
if name == "" {
return fmt.Errorf("service name cannot be empty")
}
if strings.Contains(name, " ") {
return fmt.Errorf("service name cannot contain spaces")
}
info, err := os.Stat(cfg.ExecutablePath)
if err != nil {
return fmt.Errorf("executable path does not exist: %s", cfg.ExecutablePath)
}
if info.IsDir() {
return fmt.Errorf("path is not a file: %s", cfg.ExecutablePath)
}
if !isExecutable(info) {
return fmt.Errorf("file is not executable: %s", cfg.ExecutablePath)
}
return nil
}
// platformManager is implemented once per OS (service_linux.go,
// service_darwin.go, service_windows.go).
type platformManager interface {
Install(cfg Config) error
Uninstall(name string) error
Start(name string) error
Stop(name string) error
Restart(name string) error
}
// Manager is the main service manager that delegates to a platform-specific
// implementation, mirroring service_manager.py's ServiceManager class.
type Manager struct {
configDir string
impl platformManager
}
// New creates a Manager using the appropriate platform implementation.
func New(configDir string) (*Manager, error) {
impl, err := newPlatformManager(configDir)
if err != nil {
return nil, err
}
return &Manager{configDir: configDir, impl: impl}, nil
}
// Install installs a service with the given name and executable path.
func (m *Manager) Install(name, executablePath, description, args string) error {
cfg := Config{
Name: name,
ExecutablePath: executablePath,
Description: description,
Args: args,
}
if err := validateConfig(cfg); err != nil {
return err
}
return m.impl.Install(cfg)
}
// Uninstall uninstalls a service by name.
func (m *Manager) Uninstall(name string) error {
if err := requireName(name); err != nil {
return err
}
return m.impl.Uninstall(strings.TrimSpace(name))
}
// Start starts a service by name.
func (m *Manager) Start(name string) error {
if err := requireName(name); err != nil {
return err
}
return m.impl.Start(strings.TrimSpace(name))
}
// Stop stops a service by name.
func (m *Manager) Stop(name string) error {
if err := requireName(name); err != nil {
return err
}
return m.impl.Stop(strings.TrimSpace(name))
}
// Restart restarts a service by name.
func (m *Manager) Restart(name string) error {
if err := requireName(name); err != nil {
return err
}
return m.impl.Restart(strings.TrimSpace(name))
}
func requireName(name string) error {
if strings.TrimSpace(name) == "" {
return fmt.Errorf("service name cannot be empty")
}
return nil
}