update caching and server creation

This commit is contained in:
Fran Jurmanović
2025-06-01 19:48:39 +02:00
parent 8a3b11b1ef
commit d57013bb50
26 changed files with 888 additions and 249 deletions

View File

@@ -3,35 +3,47 @@ package service
import (
"acc-server-manager/local/utl/command"
"acc-server-manager/local/utl/logging"
"context"
"fmt"
"path/filepath"
"strings"
)
const (
NSSMPath = ".\\nssm"
NSSMPath = ".\\nssm.exe"
)
type WindowsService struct {
executor *command.CommandExecutor
executor *command.CommandExecutor
configService *SystemConfigService
}
func NewWindowsService() *WindowsService {
func NewWindowsService(configService *SystemConfigService) *WindowsService {
return &WindowsService{
executor: &command.CommandExecutor{
ExePath: "powershell",
LogOutput: true,
},
configService: configService,
}
}
// executeNSSM runs an NSSM command through PowerShell with elevation
func (s *WindowsService) executeNSSM(args ...string) (string, error) {
func (s *WindowsService) executeNSSM(ctx context.Context, args ...string) (string, error) {
// Get NSSM path from config
nssmPath, err := s.configService.GetNSSMPath(ctx)
if err != nil {
return "", fmt.Errorf("failed to get NSSM path from config: %v", err)
}
// Prepend NSSM path to arguments
nssmArgs := append([]string{"-nologo", "-noprofile", NSSMPath}, args...)
nssmArgs := append([]string{"-NoProfile", "-NonInteractive", "-Command", "& " + nssmPath}, args...)
output, err := s.executor.ExecuteWithOutput(nssmArgs...)
if err != nil {
// Log the full command and error for debugging
logging.Error("NSSM command failed: powershell %s", strings.Join(nssmArgs, " "))
logging.Error("NSSM error output: %s", output)
return "", err
}
@@ -45,42 +57,54 @@ func (s *WindowsService) executeNSSM(args ...string) (string, error) {
// Service Installation/Configuration Methods
func (s *WindowsService) CreateService(serviceName, execPath, workingDir string, args []string) error {
// Ensure paths are absolute
func (s *WindowsService) CreateService(ctx context.Context, serviceName, execPath, workingDir string, args []string) error {
// Ensure paths are absolute and properly formatted for Windows
absExecPath, err := filepath.Abs(execPath)
if err != nil {
return fmt.Errorf("failed to get absolute path for executable: %v", err)
}
absExecPath = filepath.Clean(absExecPath)
absWorkingDir, err := filepath.Abs(workingDir)
if err != nil {
return fmt.Errorf("failed to get absolute path for working directory: %v", err)
}
absWorkingDir = filepath.Clean(absWorkingDir)
// Log the paths being used
logging.Info("Creating service '%s' with:", serviceName)
logging.Info(" Executable: %s", absExecPath)
logging.Info(" Working Directory: %s", absWorkingDir)
// First remove any existing service with the same name
s.executeNSSM(ctx, "remove", serviceName, "confirm")
// Install service
if _, err := s.executeNSSM("install", serviceName, absExecPath); err != nil {
if _, err := s.executeNSSM(ctx, "install", serviceName, absExecPath); err != nil {
return fmt.Errorf("failed to install service: %v", err)
}
// Set working directory
if _, err := s.executeNSSM("set", serviceName, "AppDirectory", absWorkingDir); err != nil {
return fmt.Errorf("failed to set working directory: %v", err)
}
// Set arguments if provided
if len(args) > 0 {
cmdArgs := append([]string{"set", serviceName, "AppParameters"}, args...)
if _, err := s.executeNSSM(cmdArgs...); err != nil {
if _, err := s.executeNSSM(ctx, cmdArgs...); err != nil {
// Try to clean up on failure
s.executeNSSM(ctx, "remove", serviceName, "confirm")
return fmt.Errorf("failed to set arguments: %v", err)
}
}
// Verify service was created
if _, err := s.executeNSSM(ctx, "get", serviceName, "Application"); err != nil {
return fmt.Errorf("service creation verification failed: %v", err)
}
logging.Info("Created Windows service: %s", serviceName)
return nil
}
func (s *WindowsService) DeleteService(serviceName string) error {
if _, err := s.executeNSSM("remove", serviceName, "confirm"); err != nil {
func (s *WindowsService) DeleteService(ctx context.Context, serviceName string) error {
if _, err := s.executeNSSM(ctx, "remove", serviceName, "confirm"); err != nil {
return fmt.Errorf("failed to remove service: %v", err)
}
@@ -88,36 +112,36 @@ func (s *WindowsService) DeleteService(serviceName string) error {
return nil
}
func (s *WindowsService) UpdateService(serviceName, execPath, workingDir string, args []string) error {
func (s *WindowsService) UpdateService(ctx context.Context, serviceName, execPath, workingDir string, args []string) error {
// First remove the existing service
if err := s.DeleteService(serviceName); err != nil {
if err := s.DeleteService(ctx, serviceName); err != nil {
return err
}
// Then create it again with new parameters
return s.CreateService(serviceName, execPath, workingDir, args)
return s.CreateService(ctx, serviceName, execPath, workingDir, args)
}
// Service Control Methods
func (s *WindowsService) Status(serviceName string) (string, error) {
return s.executeNSSM("status", serviceName)
func (s *WindowsService) Status(ctx context.Context, serviceName string) (string, error) {
return s.executeNSSM(ctx, "status", serviceName)
}
func (s *WindowsService) Start(serviceName string) (string, error) {
return s.executeNSSM("start", serviceName)
func (s *WindowsService) Start(ctx context.Context, serviceName string) (string, error) {
return s.executeNSSM(ctx, "start", serviceName)
}
func (s *WindowsService) Stop(serviceName string) (string, error) {
return s.executeNSSM("stop", serviceName)
func (s *WindowsService) Stop(ctx context.Context, serviceName string) (string, error) {
return s.executeNSSM(ctx, "stop", serviceName)
}
func (s *WindowsService) Restart(serviceName string) (string, error) {
func (s *WindowsService) Restart(ctx context.Context, serviceName string) (string, error) {
// First stop the service
if _, err := s.Stop(serviceName); err != nil {
if _, err := s.Stop(ctx, serviceName); err != nil {
return "", err
}
// Then start it again
return s.Start(serviceName)
return s.Start(ctx, serviceName)
}