From a41af2618715f0166aa84cce54341772697e1722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Thu, 18 Sep 2025 22:04:51 +0200 Subject: [PATCH] add step list for server creation --- local/service/config.go | 230 +++++++++++++--------------- local/service/server.go | 328 ++++++++++++++++------------------------ 2 files changed, 231 insertions(+), 327 deletions(-) diff --git a/local/service/config.go b/local/service/config.go index 97144c5..09363f1 100644 --- a/local/service/config.go +++ b/local/service/config.go @@ -99,6 +99,62 @@ func (as *ConfigService) UpdateConfig(ctx *fiber.Ctx, body *map[string]interface return as.updateConfigInternal(ctx.UserContext(), serverID, configFile, body, override) } +func (as *ConfigService) updateConfigFiles(ctx context.Context, server *model.Server, configFile string, body *map[string]interface{}, override bool) ([]byte, []byte, error) { + if server == nil { + logging.Error("Server not found") + return nil, nil, fmt.Errorf("server not found") + } + + configPath := filepath.Join(server.GetConfigPath(), configFile) + oldData, err := os.ReadFile(configPath) + if err != nil { + if os.IsNotExist(err) { + dir := filepath.Dir(configPath) + if err := os.MkdirAll(dir, 0755); err != nil { + return nil, nil, err + } + if err := os.WriteFile(configPath, []byte("{}"), 0644); err != nil { + return nil, nil, err + } + oldData = []byte("{}") + } else { + return nil, nil, err + } + } + + oldDataUTF8, err := DecodeUTF16LEBOM(oldData) + if err != nil { + return nil, nil, err + } + + newData, err := json.Marshal(&body) + if err != nil { + return nil, nil, err + } + + if !override { + newData, err = jsons.Merge(oldDataUTF8, newData) + if err != nil { + return nil, nil, err + } + } + newData, err = common.IndentJson(newData) + if err != nil { + return nil, nil, err + } + + newDataUTF16, err := EncodeUTF16LEBOM(newData) + if err != nil { + return nil, nil, err + } + + if err := os.WriteFile(configPath, newDataUTF16, 0644); err != nil { + return nil, nil, err + } + + return oldDataUTF8, newData, nil +} + func (as *ConfigService) updateConfigInternal(ctx context.Context, serverID string, configFile string, body *map[string]interface{}, override bool) (*model.Config, error) { serverUUID, err := uuid.Parse(serverID) if err != nil { @@ -112,50 +168,8 @@ func (as *ConfigService) updateConfigInternal(ctx context.Context, serverID stri return nil, fmt.Errorf("server not found") } - configPath := filepath.Join(server.GetConfigPath(), configFile) - oldData, err := os.ReadFile(configPath) + oldDataUTF8, newData, err := as.updateConfigFiles(ctx, server, configFile, body, override) if err != nil { - if os.IsNotExist(err) { - dir := filepath.Dir(configPath) - if err := os.MkdirAll(dir, 0755); err != nil { - return nil, err - } - if err := os.WriteFile(configPath, []byte("{}"), 0644); err != nil { - return nil, err - } - oldData = []byte("{}") - } else { - return nil, err - } - } - - oldDataUTF8, err := DecodeUTF16LEBOM(oldData) - if err != nil { - return nil, err - } - - newData, err := json.Marshal(&body) - if err != nil { - return nil, err - } - - if !override { - newData, err = jsons.Merge(oldDataUTF8, newData) - if err != nil { - return nil, err - } - } - newData, err = common.IndentJson(newData) - if err != nil { - return nil, err - } - - newDataUTF16, err := EncodeUTF16LEBOM(newData) - if err != nil { - return nil, err - } - - if err := os.WriteFile(configPath, newDataUTF16, 0644); err != nil { return nil, err } @@ -181,42 +195,47 @@ func (as *ConfigService) GetConfig(ctx *fiber.Ctx) (interface{}, error) { logging.Debug("Getting config for server ID: %s, file: %s", serverIDStr, configFile) server, err := as.serverRepository.GetByID(ctx.UserContext(), serverIDStr) + if err != nil { logging.Error("Server not found") return nil, fiber.NewError(404, "Server not found") } + return as.getConfigFile(server, configFile) +} +func (as *ConfigService) getConfigFile(server *model.Server, configFile string) (interface{}, error) { + serverIDStr := server.ID.String() switch configFile { case ConfigurationJson: if cached, ok := as.configCache.GetConfiguration(serverIDStr); ok { logging.Debug("Returning cached configuration for server ID: %s", serverIDStr) - return cached, nil + return *cached, nil } case AssistRulesJson: if cached, ok := as.configCache.GetAssistRules(serverIDStr); ok { logging.Debug("Returning cached assist rules for server ID: %s", serverIDStr) - return cached, nil + return *cached, nil } case EventJson: if cached, ok := as.configCache.GetEvent(serverIDStr); ok { logging.Debug("Returning cached event config for server ID: %s", serverIDStr) - return cached, nil + return *cached, nil } case EventRulesJson: if cached, ok := as.configCache.GetEventRules(serverIDStr); ok { logging.Debug("Returning cached event rules for server ID: %s", serverIDStr) - return cached, nil + return *cached, nil } case SettingsJson: if cached, ok := as.configCache.GetSettings(serverIDStr); ok { logging.Debug("Returning cached settings for server ID: %s", serverIDStr) - return cached, nil + return *cached, nil } } logging.Debug("Cache miss for server ID: %s, file: %s - loading from disk", serverIDStr, configFile) - configPath := filepath.Join(server.GetConfigPath(), configFile) + configPath := server.GetConfigPath() decoder := DecodeFileName(configFile) if decoder == nil { return nil, errors.New("invalid config file") @@ -228,15 +247,15 @@ func (as *ConfigService) GetConfig(ctx *fiber.Ctx) (interface{}, error) { logging.Debug("Config file not found, creating default for server ID: %s, file: %s", serverIDStr, configFile) switch configFile { case ConfigurationJson: - return &model.Configuration{}, nil + return model.Configuration{}, nil case AssistRulesJson: - return &model.AssistRules{}, nil + return model.AssistRules{}, nil case EventJson: - return &model.EventConfig{}, nil + return model.EventConfig{}, nil case EventRulesJson: - return &model.EventRules{}, nil + return model.EventRules{}, nil case SettingsJson: - return &model.ServerSettings{}, nil + return model.ServerSettings{}, nil } } return nil, err @@ -244,15 +263,15 @@ func (as *ConfigService) GetConfig(ctx *fiber.Ctx) (interface{}, error) { switch configFile { case ConfigurationJson: - as.configCache.UpdateConfiguration(serverIDStr, *config.(*model.Configuration)) + as.configCache.UpdateConfiguration(serverIDStr, config.(model.Configuration)) case AssistRulesJson: - as.configCache.UpdateAssistRules(serverIDStr, *config.(*model.AssistRules)) + as.configCache.UpdateAssistRules(serverIDStr, config.(model.AssistRules)) case EventJson: - as.configCache.UpdateEvent(serverIDStr, *config.(*model.EventConfig)) + as.configCache.UpdateEvent(serverIDStr, config.(model.EventConfig)) case EventRulesJson: - as.configCache.UpdateEventRules(serverIDStr, *config.(*model.EventRules)) + as.configCache.UpdateEventRules(serverIDStr, config.(model.EventRules)) case SettingsJson: - as.configCache.UpdateSettings(serverIDStr, *config.(*model.ServerSettings)) + as.configCache.UpdateSettings(serverIDStr, config.(model.ServerSettings)) } logging.Debug("Successfully loaded and cached config for server ID: %s, file: %s", serverIDStr, configFile) @@ -274,77 +293,33 @@ func (as *ConfigService) GetConfigs(ctx *fiber.Ctx) (*model.Configurations, erro func (as *ConfigService) LoadConfigs(server *model.Server) (*model.Configurations, error) { serverIDStr := server.ID.String() logging.Info("Loading configs for server ID: %s at path: %s", serverIDStr, server.GetConfigPath()) - configs := &model.Configurations{} - if cached, ok := as.configCache.GetConfiguration(serverIDStr); ok { - logging.Debug("Using cached configuration for server %s", serverIDStr) - configs.Configuration = *cached - } else { - logging.Debug("Loading configuration from disk for server %s", serverIDStr) - config, err := mustDecode[model.Configuration](ConfigurationJson, server.GetConfigPath()) - if err != nil { - logging.Error("Failed to load configuration for server %s: %v", serverIDStr, err) - return nil, fmt.Errorf("failed to load configuration: %v", err) - } - configs.Configuration = config - as.configCache.UpdateConfiguration(serverIDStr, config) + settingsConf, err := as.getConfigFile(server, SettingsJson) + if err != nil { + return nil, err } - - if cached, ok := as.configCache.GetAssistRules(serverIDStr); ok { - logging.Debug("Using cached assist rules for server %s", serverIDStr) - configs.AssistRules = *cached - } else { - logging.Debug("Loading assist rules from disk for server %s", serverIDStr) - rules, err := mustDecode[model.AssistRules](AssistRulesJson, server.GetConfigPath()) - if err != nil { - logging.Error("Failed to load assist rules for server %s: %v", serverIDStr, err) - return nil, fmt.Errorf("failed to load assist rules: %v", err) - } - configs.AssistRules = rules - as.configCache.UpdateAssistRules(serverIDStr, rules) + eventRulesConf, err := as.getConfigFile(server, EventRulesJson) + if err != nil { + return nil, err } - - if cached, ok := as.configCache.GetEvent(serverIDStr); ok { - logging.Debug("Using cached event config for server %s", serverIDStr) - configs.Event = *cached - } else { - logging.Debug("Loading event config from disk for server %s", serverIDStr) - event, err := mustDecode[model.EventConfig](EventJson, server.GetConfigPath()) - if err != nil { - logging.Error("Failed to load event config for server %s: %v", serverIDStr, err) - return nil, fmt.Errorf("failed to load event config: %v", err) - } - configs.Event = event - logging.Debug("Updating event config for server %s with track: %s", serverIDStr, event.Track) - as.configCache.UpdateEvent(serverIDStr, event) + eventConf, err := as.getConfigFile(server, EventJson) + if err != nil { + return nil, err } - - if cached, ok := as.configCache.GetEventRules(serverIDStr); ok { - logging.Debug("Using cached event rules for server %s", serverIDStr) - configs.EventRules = *cached - } else { - logging.Debug("Loading event rules from disk for server %s", serverIDStr) - rules, err := mustDecode[model.EventRules](EventRulesJson, server.GetConfigPath()) - if err != nil { - logging.Error("Failed to load event rules for server %s: %v", serverIDStr, err) - return nil, fmt.Errorf("failed to load event rules: %v", err) - } - configs.EventRules = rules - as.configCache.UpdateEventRules(serverIDStr, rules) + assistRulesConf, err := as.getConfigFile(server, AssistRulesJson) + if err != nil { + return nil, err } - - if cached, ok := as.configCache.GetSettings(serverIDStr); ok { - logging.Debug("Using cached settings for server %s", serverIDStr) - configs.Settings = *cached - } else { - logging.Debug("Loading settings from disk for server %s", serverIDStr) - settings, err := mustDecode[model.ServerSettings](SettingsJson, server.GetConfigPath()) - if err != nil { - logging.Error("Failed to load settings for server %s: %v", serverIDStr, err) - return nil, fmt.Errorf("failed to load settings: %v", err) - } - configs.Settings = settings - as.configCache.UpdateSettings(serverIDStr, settings) + configurationConf, err := as.getConfigFile(server, ConfigurationJson) + if err != nil { + return nil, err + } + configs := &model.Configurations{ + Settings: settingsConf.(model.ServerSettings), + EventRules: eventRulesConf.(model.EventRules), + Event: eventConf.(model.EventConfig), + AssistRules: assistRulesConf.(model.AssistRules), + Configuration: configurationConf.(model.Configuration), } logging.Info("Successfully loaded all configs for server %s", serverIDStr) @@ -369,9 +344,6 @@ func readFile(path string, configFile string) ([]byte, error) { configPath := filepath.Join(path, configFile) oldData, err := os.ReadFile(configPath) if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("config file %s does not exist at %s", configFile, configPath) - } return nil, err } return oldData, nil @@ -458,6 +430,6 @@ func (as *ConfigService) SaveConfiguration(server *model.Server, config *model.C return fmt.Errorf("failed to unmarshal configuration: %v", err) } - _, err = as.updateConfigInternal(context.Background(), server.ID.String(), ConfigurationJson, &configMap, true) + _, _, err = as.updateConfigFiles(context.Background(), server, ConfigurationJson, &configMap, true) return err } diff --git a/local/service/server.go b/local/service/server.go index b7ac070..00adc45 100644 --- a/local/service/server.go +++ b/local/service/server.go @@ -308,7 +308,9 @@ func (as *ServerService) GetById(ctx *fiber.Ctx, serverID uuid.UUID) (*model.Ser } func (s *ServerService) CreateServerAsync(ctx *fiber.Ctx, server *model.Server) error { + logging.Info("create server start") if err := server.Validate(); err != nil { + logging.Info("create server validation failed") return err } @@ -317,6 +319,7 @@ func (s *ServerService) CreateServerAsync(ctx *fiber.Ctx, server *model.Server) bgCtx := context.Background() go func() { + logging.Info("create server start background") if err := s.createServerBackground(bgCtx, server); err != nil { logging.Error("Async server creation failed for server %s: %v", server.ID, err) s.webSocketService.BroadcastError(server.ID, "Server creation failed", err.Error()) @@ -327,105 +330,126 @@ func (s *ServerService) CreateServerAsync(ctx *fiber.Ctx, server *model.Server) return nil } -func (s *ServerService) CreateServer(ctx *fiber.Ctx, server *model.Server) error { - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusInProgress, - model.GetStepDescription(model.StepValidation), "") +type createServerStep struct { + stepType model.ServerCreationStep + important bool + callback func() (string, error) + description string +} - if err := server.Validate(); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusFailed, - "", fmt.Sprintf("Validation failed: %v", err)) - return err +func (s *ServerService) createServerBackground(ctx context.Context, server *model.Server) error { + var serverPort int + var tcpPorts, udpPorts []int + + steps := []createServerStep{ + { + stepType: model.StepValidation, + important: true, + description: "Server configuration validated successfully", + callback: func() (string, error) { + if err := server.Validate(); err != nil { + return "", fmt.Errorf("validation failed: %v", err) + } + return "Server configuration validated successfully", nil + }, + }, + { + stepType: model.StepDirectoryCreation, + important: true, + description: "Server directories prepared", + callback: func() (string, error) { + return "Server directories prepared", nil + }, + }, + { + stepType: model.StepSteamDownload, + important: true, + description: "Server files downloaded successfully", + callback: func() (string, error) { + if err := s.steamService.InstallServerWithWebSocket(ctx, server.Path, &server.ID, s.webSocketService); err != nil { + return "", fmt.Errorf("failed to install server: %v", err) + } + return "Server files downloaded successfully", nil + }, + }, + { + stepType: model.StepConfigGeneration, + important: true, + description: "", + callback: func() (string, error) { + ports, err := network.FindAvailablePortRange(DefaultStartPort, RequiredPortCount) + if err != nil { + return "", fmt.Errorf("failed to find available ports: %v", err) + } + + serverPort = ports[0] + + if err := s.updateServerPort(server, serverPort); err != nil { + return "", fmt.Errorf("failed to update server configuration: %v", err) + } + + return fmt.Sprintf("Server configuration generated (Port: %d)", serverPort), nil + }, + }, + { + stepType: model.StepServiceCreation, + important: true, + description: "", + callback: func() (string, error) { + execPath := filepath.Join(server.GetServerPath(), "accServer.exe") + serverWorkingDir := filepath.Join(server.GetServerPath(), "server") + if err := s.windowsService.CreateService(ctx, server.ServiceName, execPath, serverWorkingDir, nil); err != nil { + return "", fmt.Errorf("failed to create Windows service: %v", err) + } + return fmt.Sprintf("Windows service '%s' created successfully", server.ServiceName), nil + }, + }, + { + stepType: model.StepFirewallRules, + important: false, + description: "", + callback: func() (string, error) { + s.configureFirewall(server) + tcpPorts = []int{serverPort} + udpPorts = []int{serverPort} + if err := s.firewallService.CreateServerRules(server.ServiceName, tcpPorts, udpPorts); err != nil { + return "", fmt.Errorf("failed to create firewall rules: %v", err) + } + return fmt.Sprintf("Firewall rules created for port %d", serverPort), nil + }, + }, + { + stepType: model.StepDatabaseSave, + important: true, + description: "Server saved to database successfully", + callback: func() (string, error) { + if err := s.repository.Insert(ctx, server); err != nil { + return "", fmt.Errorf("failed to insert server into database: %v", err) + } + return "Server saved to database successfully", nil + }, + }, } - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusCompleted, - "Server configuration validated successfully", "") + for i, step := range steps { + s.webSocketService.BroadcastStep(server.ID, step.stepType, model.StatusInProgress, + model.GetStepDescription(step.stepType), "") - s.webSocketService.BroadcastStep(server.ID, model.StepDirectoryCreation, model.StatusInProgress, - model.GetStepDescription(model.StepDirectoryCreation), "") + successMessage, err := step.callback() + if err != nil { + s.webSocketService.BroadcastStep(server.ID, step.stepType, model.StatusFailed, + "", err.Error()) - s.webSocketService.BroadcastStep(server.ID, model.StepDirectoryCreation, model.StatusCompleted, - "Server directories prepared", "") + if step.important { + s.rollbackSteps(ctx, server, steps[:i], tcpPorts, udpPorts) + return err + } + } - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusInProgress, - model.GetStepDescription(model.StepSteamDownload), "") - - if err := s.steamService.InstallServerWithWebSocket(ctx.UserContext(), server.Path, &server.ID, s.webSocketService); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusFailed, - "", fmt.Sprintf("Steam installation failed: %v", err)) - return fmt.Errorf("failed to install server: %v", err) + s.webSocketService.BroadcastStep(server.ID, step.stepType, model.StatusCompleted, + successMessage, "") } - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusCompleted, - "Server files downloaded successfully", "") - - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusInProgress, - model.GetStepDescription(model.StepConfigGeneration), "") - - ports, err := network.FindAvailablePortRange(DefaultStartPort, RequiredPortCount) - if err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusFailed, - "", fmt.Sprintf("Failed to find available ports: %v", err)) - return fmt.Errorf("failed to find available ports: %v", err) - } - - serverPort := ports[0] - - if err := s.updateServerPort(server, serverPort); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusFailed, - "", fmt.Sprintf("Failed to update server configuration: %v", err)) - return fmt.Errorf("failed to update server configuration: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusCompleted, - fmt.Sprintf("Server configuration generated (Port: %d)", serverPort), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusInProgress, - model.GetStepDescription(model.StepServiceCreation), "") - - execPath := filepath.Join(server.GetServerPath(), "accServer.exe") - serverWorkingDir := filepath.Join(server.GetServerPath(), "server") - if err := s.windowsService.CreateService(ctx.UserContext(), server.ServiceName, execPath, serverWorkingDir, nil); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusFailed, - "", fmt.Sprintf("Failed to create Windows service: %v", err)) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to create Windows service: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusCompleted, - fmt.Sprintf("Windows service '%s' created successfully", server.ServiceName), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusInProgress, - model.GetStepDescription(model.StepFirewallRules), "") - - s.configureFirewall(server) - tcpPorts := []int{serverPort} - udpPorts := []int{serverPort} - if err := s.firewallService.CreateServerRules(server.ServiceName, tcpPorts, udpPorts); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusFailed, - "", fmt.Sprintf("Failed to create firewall rules: %v", err)) - s.windowsService.DeleteService(ctx.UserContext(), server.ServiceName) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to create firewall rules: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusCompleted, - fmt.Sprintf("Firewall rules created for port %d", serverPort), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusInProgress, - model.GetStepDescription(model.StepDatabaseSave), "") - - if err := s.repository.Insert(ctx.UserContext(), server); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusFailed, - "", fmt.Sprintf("Failed to save server to database: %v", err)) - s.firewallService.DeleteServerRules(server.ServiceName, tcpPorts, udpPorts) - s.windowsService.DeleteService(ctx.UserContext(), server.ServiceName) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to insert server into database: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusCompleted, - "Server saved to database successfully", "") - s.StartAccServerRuntime(server) s.webSocketService.BroadcastStep(server.ID, model.StepCompleted, model.StatusCompleted, @@ -437,114 +461,22 @@ func (s *ServerService) CreateServer(ctx *fiber.Ctx, server *model.Server) error return nil } -func (s *ServerService) createServerBackground(ctx context.Context, server *model.Server) error { - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusInProgress, - model.GetStepDescription(model.StepValidation), "") - - if err := server.Validate(); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusFailed, - "", fmt.Sprintf("Validation failed: %v", err)) - return err +func (s *ServerService) rollbackSteps(ctx context.Context, server *model.Server, completedSteps []createServerStep, tcpPorts, udpPorts []int) { + for i := len(completedSteps) - 1; i >= 0; i-- { + step := completedSteps[i] + switch step.stepType { + case model.StepDatabaseSave: + s.repository.Delete(ctx, server.ID) + case model.StepFirewallRules: + if len(tcpPorts) > 0 && len(udpPorts) > 0 { + s.firewallService.DeleteServerRules(server.ServiceName, tcpPorts, udpPorts) + } + case model.StepServiceCreation: + s.windowsService.DeleteService(ctx, server.ServiceName) + case model.StepSteamDownload: + s.steamService.UninstallServer(server.Path) + } } - - s.webSocketService.BroadcastStep(server.ID, model.StepValidation, model.StatusCompleted, - "Server configuration validated successfully", "") - - s.webSocketService.BroadcastStep(server.ID, model.StepDirectoryCreation, model.StatusInProgress, - model.GetStepDescription(model.StepDirectoryCreation), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepDirectoryCreation, model.StatusCompleted, - "Server directories prepared", "") - - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusInProgress, - model.GetStepDescription(model.StepSteamDownload), "") - - if err := s.steamService.InstallServerWithWebSocket(ctx, server.Path, &server.ID, s.webSocketService); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusFailed, - "", fmt.Sprintf("Steam installation failed: %v", err)) - return fmt.Errorf("failed to install server: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepSteamDownload, model.StatusCompleted, - "Server files downloaded successfully", "") - - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusInProgress, - model.GetStepDescription(model.StepConfigGeneration), "") - - ports, err := network.FindAvailablePortRange(DefaultStartPort, RequiredPortCount) - if err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusFailed, - "", fmt.Sprintf("Failed to find available ports: %v", err)) - return fmt.Errorf("failed to find available ports: %v", err) - } - - serverPort := ports[0] - - if err := s.updateServerPort(server, serverPort); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusFailed, - "", fmt.Sprintf("Failed to update server configuration: %v", err)) - return fmt.Errorf("failed to update server configuration: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepConfigGeneration, model.StatusCompleted, - fmt.Sprintf("Server configuration generated (Port: %d)", serverPort), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusInProgress, - model.GetStepDescription(model.StepServiceCreation), "") - - execPath := filepath.Join(server.GetServerPath(), "accServer.exe") - serverWorkingDir := filepath.Join(server.GetServerPath(), "server") - if err := s.windowsService.CreateService(ctx, server.ServiceName, execPath, serverWorkingDir, nil); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusFailed, - "", fmt.Sprintf("Failed to create Windows service: %v", err)) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to create Windows service: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepServiceCreation, model.StatusCompleted, - fmt.Sprintf("Windows service '%s' created successfully", server.ServiceName), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusInProgress, - model.GetStepDescription(model.StepFirewallRules), "") - - s.configureFirewall(server) - tcpPorts := []int{serverPort} - udpPorts := []int{serverPort} - if err := s.firewallService.CreateServerRules(server.ServiceName, tcpPorts, udpPorts); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusFailed, - "", fmt.Sprintf("Failed to create firewall rules: %v", err)) - s.windowsService.DeleteService(ctx, server.ServiceName) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to create firewall rules: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepFirewallRules, model.StatusCompleted, - fmt.Sprintf("Firewall rules created for port %d", serverPort), "") - - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusInProgress, - model.GetStepDescription(model.StepDatabaseSave), "") - - if err := s.repository.Insert(ctx, server); err != nil { - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusFailed, - "", fmt.Sprintf("Failed to save server to database: %v", err)) - s.firewallService.DeleteServerRules(server.ServiceName, tcpPorts, udpPorts) - s.windowsService.DeleteService(ctx, server.ServiceName) - s.steamService.UninstallServer(server.Path) - return fmt.Errorf("failed to insert server into database: %v", err) - } - - s.webSocketService.BroadcastStep(server.ID, model.StepDatabaseSave, model.StatusCompleted, - "Server saved to database successfully", "") - - s.StartAccServerRuntime(server) - - s.webSocketService.BroadcastStep(server.ID, model.StepCompleted, model.StatusCompleted, - model.GetStepDescription(model.StepCompleted), "") - - s.webSocketService.BroadcastComplete(server.ID, true, - fmt.Sprintf("Server '%s' created successfully on port %d", server.Name, serverPort)) - - return nil } func (s *ServerService) DeleteServer(ctx *fiber.Ctx, serverID uuid.UUID) error {