remove system config
This commit is contained in:
@@ -19,8 +19,7 @@ type ApiService struct {
|
||||
}
|
||||
|
||||
func NewApiService(repository *repository.ApiRepository,
|
||||
serverRepository *repository.ServerRepository,
|
||||
systemConfigService *SystemConfigService) *ApiService {
|
||||
serverRepository *repository.ServerRepository) *ApiService {
|
||||
return &ApiService{
|
||||
repository: repository,
|
||||
serverRepository: serverRepository,
|
||||
@@ -29,7 +28,7 @@ func NewApiService(repository *repository.ApiRepository,
|
||||
ThrottleTime: 5 * time.Second, // Minimum 5 seconds between checks
|
||||
DefaultStatus: model.StatusRunning, // Default to running if throttled
|
||||
}),
|
||||
windowsService: NewWindowsService(systemConfigService),
|
||||
windowsService: NewWindowsService(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,18 +12,31 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// CacheInvalidator interface for cache invalidation
|
||||
type CacheInvalidator interface {
|
||||
InvalidateUserPermissions(userID string)
|
||||
InvalidateAllUserPermissions()
|
||||
}
|
||||
|
||||
// MembershipService provides business logic for membership-related operations.
|
||||
type MembershipService struct {
|
||||
repo *repository.MembershipRepository
|
||||
repo *repository.MembershipRepository
|
||||
cacheInvalidator CacheInvalidator
|
||||
}
|
||||
|
||||
// NewMembershipService creates a new MembershipService.
|
||||
func NewMembershipService(repo *repository.MembershipRepository) *MembershipService {
|
||||
return &MembershipService{
|
||||
repo: repo,
|
||||
repo: repo,
|
||||
cacheInvalidator: nil, // Will be set later via SetCacheInvalidator
|
||||
}
|
||||
}
|
||||
|
||||
// SetCacheInvalidator sets the cache invalidator after service initialization
|
||||
func (s *MembershipService) SetCacheInvalidator(invalidator CacheInvalidator) {
|
||||
s.cacheInvalidator = invalidator
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a JWT.
|
||||
func (s *MembershipService) Login(ctx context.Context, username, password string) (string, error) {
|
||||
user, err := s.repo.FindUserByUsername(ctx, username)
|
||||
@@ -109,6 +122,11 @@ func (s *MembershipService) DeleteUser(ctx context.Context, userID uuid.UUID) er
|
||||
return err
|
||||
}
|
||||
|
||||
// Invalidate cache for deleted user
|
||||
if s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateUserPermissions(userID.String())
|
||||
}
|
||||
|
||||
logging.InfoOperation("USER_DELETE", "Deleted user: "+userID.String())
|
||||
return nil
|
||||
}
|
||||
@@ -142,6 +160,11 @@ func (s *MembershipService) UpdateUser(ctx context.Context, userID uuid.UUID, re
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Invalidate cache if role was changed
|
||||
if req.RoleID != nil && s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateUserPermissions(userID.String())
|
||||
}
|
||||
|
||||
logging.InfoOperation("USER_UPDATE", "Updated user: "+user.Username+" (ID: "+user.ID.String()+")")
|
||||
return user, nil
|
||||
}
|
||||
@@ -241,6 +264,11 @@ func (s *MembershipService) SetupInitialData(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Invalidate all caches after role setup changes
|
||||
if s.cacheInvalidator != nil {
|
||||
s.cacheInvalidator.InvalidateAllUserPermissions()
|
||||
}
|
||||
|
||||
// Create a default admin user if one doesn't exist
|
||||
_, err = s.repo.FindUserByUsername(ctx, "admin")
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/repository"
|
||||
"acc-server-manager/local/utl/env"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"acc-server-manager/local/utl/tracking"
|
||||
"context"
|
||||
@@ -23,19 +24,18 @@ const (
|
||||
)
|
||||
|
||||
type ServerService struct {
|
||||
repository *repository.ServerRepository
|
||||
stateHistoryRepo *repository.StateHistoryRepository
|
||||
apiService *ApiService
|
||||
configService *ConfigService
|
||||
steamService *SteamService
|
||||
windowsService *WindowsService
|
||||
firewallService *FirewallService
|
||||
systemConfigService *SystemConfigService
|
||||
instances sync.Map // Track instances per server
|
||||
lastInsertTimes sync.Map // Track last insert time per server
|
||||
debouncers sync.Map // Track debounce timers per server
|
||||
logTailers sync.Map // Track log tailers per server
|
||||
sessionIDs sync.Map // Track current session ID per server
|
||||
repository *repository.ServerRepository
|
||||
stateHistoryRepo *repository.StateHistoryRepository
|
||||
apiService *ApiService
|
||||
configService *ConfigService
|
||||
steamService *SteamService
|
||||
windowsService *WindowsService
|
||||
firewallService *FirewallService
|
||||
instances sync.Map // Track instances per server
|
||||
lastInsertTimes sync.Map // Track last insert time per server
|
||||
debouncers sync.Map // Track debounce timers per server
|
||||
logTailers sync.Map // Track log tailers per server
|
||||
sessionIDs sync.Map // Track current session ID per server
|
||||
}
|
||||
|
||||
type pendingState struct {
|
||||
@@ -68,17 +68,15 @@ func NewServerService(
|
||||
steamService *SteamService,
|
||||
windowsService *WindowsService,
|
||||
firewallService *FirewallService,
|
||||
systemConfigService *SystemConfigService,
|
||||
) *ServerService {
|
||||
service := &ServerService{
|
||||
repository: repository,
|
||||
stateHistoryRepo: stateHistoryRepo,
|
||||
apiService: apiService,
|
||||
configService: configService,
|
||||
steamService: steamService,
|
||||
windowsService: windowsService,
|
||||
firewallService: firewallService,
|
||||
systemConfigService: systemConfigService,
|
||||
repository: repository,
|
||||
stateHistoryRepo: stateHistoryRepo,
|
||||
apiService: apiService,
|
||||
configService: configService,
|
||||
steamService: steamService,
|
||||
windowsService: windowsService,
|
||||
firewallService: firewallService,
|
||||
}
|
||||
|
||||
// Initialize server instances
|
||||
@@ -203,13 +201,8 @@ func (s *ServerService) updateSessionDuration(server *model.Server, sessionType
|
||||
}
|
||||
|
||||
func (s *ServerService) GenerateServerPath(server *model.Server) {
|
||||
// Get the base steamcmd path
|
||||
steamCMDPath, err := s.systemConfigService.GetSteamCMDDirPath(context.Background())
|
||||
if err != nil {
|
||||
logging.Error("Failed to get steamcmd path: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the base steamcmd path from environment variable
|
||||
steamCMDPath := env.GetSteamCMDDirPath()
|
||||
server.Path = server.GenerateServerPath(steamCMDPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,13 @@ func InitializeServices(c *dig.Container) {
|
||||
c.Provide(NewApiService)
|
||||
c.Provide(NewConfigService)
|
||||
c.Provide(NewLookupService)
|
||||
c.Provide(NewSystemConfigService)
|
||||
c.Provide(NewSteamService)
|
||||
c.Provide(NewWindowsService)
|
||||
c.Provide(NewFirewallService)
|
||||
c.Provide(NewMembershipService)
|
||||
|
||||
logging.Debug("Initializing service dependencies")
|
||||
err := c.Invoke(func(server *ServerService, api *ApiService, config *ConfigService, systemConfig *SystemConfigService) {
|
||||
err := c.Invoke(func(server *ServerService, api *ApiService, config *ConfigService) {
|
||||
logging.Debug("Setting up service cross-references")
|
||||
api.SetServerService(server)
|
||||
config.SetServerService(server)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/repository"
|
||||
"acc-server-manager/local/utl/command"
|
||||
"acc-server-manager/local/utl/env"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -12,23 +13,21 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ACCServerAppID = "1430110"
|
||||
ACCServerAppID = "1430110"
|
||||
)
|
||||
|
||||
type SteamService struct {
|
||||
executor *command.CommandExecutor
|
||||
repository *repository.SteamCredentialsRepository
|
||||
configService *SystemConfigService
|
||||
executor *command.CommandExecutor
|
||||
repository *repository.SteamCredentialsRepository
|
||||
}
|
||||
|
||||
func NewSteamService(repository *repository.SteamCredentialsRepository, configService *SystemConfigService) *SteamService {
|
||||
func NewSteamService(repository *repository.SteamCredentialsRepository) *SteamService {
|
||||
return &SteamService{
|
||||
executor: &command.CommandExecutor{
|
||||
ExePath: "powershell",
|
||||
LogOutput: true,
|
||||
},
|
||||
repository: repository,
|
||||
configService: configService,
|
||||
repository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +43,8 @@ func (s *SteamService) SaveCredentials(ctx context.Context, creds *model.SteamCr
|
||||
}
|
||||
|
||||
func (s *SteamService) ensureSteamCMD(ctx context.Context) error {
|
||||
// Get SteamCMD path from config
|
||||
steamCMDPath, err := s.configService.GetSteamCMDDirPath(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get SteamCMD path from config: %v", err)
|
||||
}
|
||||
|
||||
// Get SteamCMD path from environment variable
|
||||
steamCMDPath := env.GetSteamCMDPath()
|
||||
steamCMDDir := filepath.Dir(steamCMDPath)
|
||||
|
||||
// Check if SteamCMD exists
|
||||
@@ -104,11 +99,8 @@ func (s *SteamService) InstallServer(ctx context.Context, installPath string) er
|
||||
return fmt.Errorf("failed to get Steam credentials: %v", err)
|
||||
}
|
||||
|
||||
// Get SteamCMD path from config
|
||||
steamCMDPath, err := s.configService.GetSteamCMDPath(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get SteamCMD path from config: %v", err)
|
||||
}
|
||||
// Get SteamCMD path from environment variable
|
||||
steamCMDPath := env.GetSteamCMDPath()
|
||||
|
||||
// Build SteamCMD command
|
||||
args := []string{
|
||||
@@ -162,4 +154,4 @@ func (s *SteamService) UpdateServer(ctx context.Context, installPath string) err
|
||||
|
||||
func (s *SteamService) UninstallServer(installPath string) error {
|
||||
return os.RemoveAll(installPath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/model"
|
||||
"acc-server-manager/local/repository"
|
||||
"acc-server-manager/local/utl/cache"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
configCacheDuration = 24 * time.Hour
|
||||
)
|
||||
|
||||
type SystemConfigService struct {
|
||||
repository *repository.SystemConfigRepository
|
||||
cache *cache.InMemoryCache
|
||||
}
|
||||
|
||||
// NewSystemConfigService creates a new SystemConfigService with dependencies injected by dig
|
||||
func NewSystemConfigService(repository *repository.SystemConfigRepository, cache *cache.InMemoryCache) *SystemConfigService {
|
||||
logging.Debug("Initializing SystemConfigService")
|
||||
return &SystemConfigService{
|
||||
repository: repository,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SystemConfigService) GetConfig(ctx context.Context, key string) (*model.SystemConfig, error) {
|
||||
cacheKey := fmt.Sprintf(model.CacheKeySystemConfig, key)
|
||||
|
||||
fetcher := func() (*model.SystemConfig, error) {
|
||||
logging.Debug("Loading system config from database: %s", key)
|
||||
return s.repository.Get(ctx, key)
|
||||
}
|
||||
|
||||
return cache.GetOrSet(s.cache, cacheKey, configCacheDuration, fetcher)
|
||||
}
|
||||
|
||||
func (s *SystemConfigService) GetAllConfigs(ctx context.Context) (*[]model.SystemConfig, error) {
|
||||
logging.Debug("Loading all system configs from database")
|
||||
return s.repository.GetAll(ctx)
|
||||
}
|
||||
|
||||
func (s *SystemConfigService) UpdateConfig(ctx context.Context, config *model.SystemConfig) error {
|
||||
if err := s.repository.Update(ctx, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Invalidate cache
|
||||
cacheKey := fmt.Sprintf(model.CacheKeySystemConfig, config.Key)
|
||||
s.cache.Delete(cacheKey)
|
||||
logging.Debug("Invalidated system config in cache: %s", config.Key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SystemConfigService) GetSteamCMDDirPath(ctx context.Context) (string, error) {
|
||||
steamCMDPath, err := s.GetSteamCMDPath(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Dir(steamCMDPath), nil
|
||||
}
|
||||
|
||||
// Helper methods for common configurations
|
||||
func (s *SystemConfigService) GetSteamCMDPath(ctx context.Context) (string, error) {
|
||||
config, err := s.GetConfig(ctx, model.ConfigKeySteamCMDPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if config == nil {
|
||||
return "", nil
|
||||
}
|
||||
return config.GetEffectiveValue(), nil
|
||||
}
|
||||
|
||||
func (s *SystemConfigService) GetNSSMPath(ctx context.Context) (string, error) {
|
||||
config, err := s.GetConfig(ctx, model.ConfigKeyNSSMPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if config == nil {
|
||||
return "", nil
|
||||
}
|
||||
return config.GetEffectiveValue(), nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"acc-server-manager/local/utl/command"
|
||||
"acc-server-manager/local/utl/env"
|
||||
"acc-server-manager/local/utl/logging"
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -9,36 +10,27 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
NSSMPath = ".\\nssm.exe"
|
||||
)
|
||||
|
||||
type WindowsService struct {
|
||||
executor *command.CommandExecutor
|
||||
configService *SystemConfigService
|
||||
executor *command.CommandExecutor
|
||||
}
|
||||
|
||||
func NewWindowsService(configService *SystemConfigService) *WindowsService {
|
||||
func NewWindowsService() *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(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)
|
||||
}
|
||||
func (s *WindowsService) ExecuteNSSM(ctx context.Context, args ...string) (string, error) {
|
||||
// Get NSSM path from environment variable
|
||||
nssmPath := env.GetNSSMPath()
|
||||
|
||||
// Prepend NSSM path to arguments
|
||||
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
|
||||
@@ -51,7 +43,7 @@ func (s *WindowsService) executeNSSM(ctx context.Context, args ...string) (strin
|
||||
cleaned := strings.TrimSpace(strings.ReplaceAll(output, "\x00", ""))
|
||||
// Remove \r\n from status strings
|
||||
cleaned = strings.TrimSuffix(cleaned, "\r\n")
|
||||
|
||||
|
||||
return cleaned, nil
|
||||
}
|
||||
|
||||
@@ -77,25 +69,25 @@ func (s *WindowsService) CreateService(ctx context.Context, serviceName, execPat
|
||||
logging.Info(" Working Directory: %s", absWorkingDir)
|
||||
|
||||
// First remove any existing service with the same name
|
||||
s.executeNSSM(ctx, "remove", serviceName, "confirm")
|
||||
s.ExecuteNSSM(ctx, "remove", serviceName, "confirm")
|
||||
|
||||
// Install service
|
||||
if _, err := s.executeNSSM(ctx, "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 arguments if provided
|
||||
if len(args) > 0 {
|
||||
cmdArgs := append([]string{"set", serviceName, "AppParameters"}, args...)
|
||||
if _, err := s.executeNSSM(ctx, cmdArgs...); err != nil {
|
||||
if _, err := s.ExecuteNSSM(ctx, cmdArgs...); err != nil {
|
||||
// Try to clean up on failure
|
||||
s.executeNSSM(ctx, "remove", serviceName, "confirm")
|
||||
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 {
|
||||
if _, err := s.ExecuteNSSM(ctx, "get", serviceName, "Application"); err != nil {
|
||||
return fmt.Errorf("service creation verification failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -104,7 +96,7 @@ func (s *WindowsService) CreateService(ctx context.Context, serviceName, execPat
|
||||
}
|
||||
|
||||
func (s *WindowsService) DeleteService(ctx context.Context, serviceName string) error {
|
||||
if _, err := s.executeNSSM(ctx, "remove", serviceName, "confirm"); err != nil {
|
||||
if _, err := s.ExecuteNSSM(ctx, "remove", serviceName, "confirm"); err != nil {
|
||||
return fmt.Errorf("failed to remove service: %v", err)
|
||||
}
|
||||
|
||||
@@ -125,15 +117,15 @@ func (s *WindowsService) UpdateService(ctx context.Context, serviceName, execPat
|
||||
// Service Control Methods
|
||||
|
||||
func (s *WindowsService) Status(ctx context.Context, serviceName string) (string, error) {
|
||||
return s.executeNSSM(ctx, "status", serviceName)
|
||||
return s.ExecuteNSSM(ctx, "status", serviceName)
|
||||
}
|
||||
|
||||
func (s *WindowsService) Start(ctx context.Context, serviceName string) (string, error) {
|
||||
return s.executeNSSM(ctx, "start", serviceName)
|
||||
return s.ExecuteNSSM(ctx, "start", serviceName)
|
||||
}
|
||||
|
||||
func (s *WindowsService) Stop(ctx context.Context, serviceName string) (string, error) {
|
||||
return s.executeNSSM(ctx, "stop", serviceName)
|
||||
return s.ExecuteNSSM(ctx, "stop", serviceName)
|
||||
}
|
||||
|
||||
func (s *WindowsService) Restart(ctx context.Context, serviceName string) (string, error) {
|
||||
@@ -144,4 +136,4 @@ func (s *WindowsService) Restart(ctx context.Context, serviceName string) (strin
|
||||
|
||||
// Then start it again
|
||||
return s.Start(ctx, serviceName)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user