From 4aa67db3fd8de645210c592945b517ac6cb79dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Wed, 18 Mar 2026 19:15:19 +0100 Subject: [PATCH] add docker specific columns to server table --- .../001_upgrade_password_security.go | 0 local/migrations/002_migrate_to_uuid.go | 0 .../003_update_state_history_sessions.go | 0 local/migrations/004_fix_ddl_comments.go | 0 local/migrations/005_add_platform_columns.go | 33 +++++++++ local/model/server.go | 2 + local/utl/env/env.go | 64 ++++++++++++++--- local/utl/network/port_pool.go | 72 +++++++++++++++++++ local/utl/server/server.go | 2 +- 9 files changed, 163 insertions(+), 10 deletions(-) mode change 100644 => 100755 local/migrations/001_upgrade_password_security.go mode change 100644 => 100755 local/migrations/002_migrate_to_uuid.go mode change 100644 => 100755 local/migrations/003_update_state_history_sessions.go mode change 100644 => 100755 local/migrations/004_fix_ddl_comments.go create mode 100644 local/migrations/005_add_platform_columns.go mode change 100644 => 100755 local/model/server.go mode change 100644 => 100755 local/utl/env/env.go create mode 100644 local/utl/network/port_pool.go diff --git a/local/migrations/001_upgrade_password_security.go b/local/migrations/001_upgrade_password_security.go old mode 100644 new mode 100755 diff --git a/local/migrations/002_migrate_to_uuid.go b/local/migrations/002_migrate_to_uuid.go old mode 100644 new mode 100755 diff --git a/local/migrations/003_update_state_history_sessions.go b/local/migrations/003_update_state_history_sessions.go old mode 100644 new mode 100755 diff --git a/local/migrations/004_fix_ddl_comments.go b/local/migrations/004_fix_ddl_comments.go old mode 100644 new mode 100755 diff --git a/local/migrations/005_add_platform_columns.go b/local/migrations/005_add_platform_columns.go new file mode 100644 index 0000000..113df0f --- /dev/null +++ b/local/migrations/005_add_platform_columns.go @@ -0,0 +1,33 @@ +package migrations + +import ( + "acc-server-manager/local/utl/logging" + "fmt" + + "gorm.io/gorm" +) + +func RunAddPlatformColumnsMigration(db *gorm.DB) error { + if err := db.AutoMigrate(&MigrationRecord{}); err != nil { + return fmt.Errorf("failed to ensure migration_records table: %v", err) + } + + var record MigrationRecord + if err := db.Where("migration_name = ?", "005_add_platform_columns").First(&record).Error; err == nil { + logging.Info("Platform columns migration already applied, skipping") + return nil + } + + record = MigrationRecord{ + MigrationName: "005_add_platform_columns", + AppliedAt: "datetime('now')", + Success: true, + Notes: "Added Platform and ContainerID columns to servers table via GORM AutoMigrate", + } + if err := db.Create(&record).Error; err != nil { + return fmt.Errorf("failed to record migration 005: %v", err) + } + + logging.Info("Platform columns migration recorded") + return nil +} diff --git a/local/model/server.go b/local/model/server.go old mode 100644 new mode 100755 index bab75d6..4693d6c --- a/local/model/server.go +++ b/local/model/server.go @@ -42,6 +42,8 @@ type Server struct { Port int `gorm:"not null" json:"-"` Path string `gorm:"not null" json:"path"` ServiceName string `gorm:"not null" json:"serviceName"` + Platform string `gorm:"not null;default:'windows'" json:"platform"` + ContainerID string `gorm:"default:''" json:"-"` State *ServerState `gorm:"-" json:"state"` DateCreated time.Time `json:"dateCreated"` FromSteamCMD bool `gorm:"not null; default:true" json:"-"` diff --git a/local/utl/env/env.go b/local/utl/env/env.go old mode 100644 new mode 100755 index 0f41b84..d1e50cd --- a/local/utl/env/env.go +++ b/local/utl/env/env.go @@ -6,10 +6,26 @@ import ( ) const ( - DefaultSteamCMDPath = "c:\\steamcmd\\steamcmd.exe" - DefaultNSSMPath = ".\\nssm.exe" + DefaultSteamCMDPath = "c:\\steamcmd\\steamcmd.exe" + DefaultNSSMPath = ".\\nssm.exe" + DefaultPlatform = "windows" + DefaultACCImage = "acc-wine:latest" + DefaultACCGameVolume = "/data/acc-game" + DefaultACCConfigBasePath = "/data/configs" + DefaultLinuxSteamCMDPath = "/usr/games/steamcmd" ) +func GetPlatform() string { + if p := os.Getenv("PLATFORM"); p != "" { + return p + } + return DefaultPlatform +} + +func IsDockerPlatform() bool { + return GetPlatform() == "docker" +} + func GetSteamCMDPath() string { if path := os.Getenv("STEAMCMD_PATH"); path != "" { return path @@ -17,6 +33,13 @@ func GetSteamCMDPath() string { return DefaultSteamCMDPath } +func GetLinuxSteamCMDPath() string { + if path := os.Getenv("STEAMCMD_LINUX_PATH"); path != "" { + return path + } + return DefaultLinuxSteamCMDPath +} + func GetSteamCMDDirPath() string { steamCMDPath := GetSteamCMDPath() return filepath.Dir(steamCMDPath) @@ -29,17 +52,40 @@ func GetNSSMPath() string { return DefaultNSSMPath } +func GetACCImage() string { + if img := os.Getenv("ACC_IMAGE"); img != "" { + return img + } + return DefaultACCImage +} + +func GetACCGameVolume() string { + if v := os.Getenv("ACC_GAME_VOLUME"); v != "" { + return v + } + return DefaultACCGameVolume +} + +func GetACCConfigBasePath() string { + if p := os.Getenv("ACC_CONFIG_BASE_PATH"); p != "" { + return p + } + return DefaultACCConfigBasePath +} + func ValidatePaths() map[string]error { errors := make(map[string]error) - steamCMDPath := GetSteamCMDPath() - if _, err := os.Stat(steamCMDPath); os.IsNotExist(err) { - errors["STEAMCMD_PATH"] = err - } + if !IsDockerPlatform() { + steamCMDPath := GetSteamCMDPath() + if _, err := os.Stat(steamCMDPath); os.IsNotExist(err) { + errors["STEAMCMD_PATH"] = err + } - nssmPath := GetNSSMPath() - if _, err := os.Stat(nssmPath); os.IsNotExist(err) { - errors["NSSM_PATH"] = err + nssmPath := GetNSSMPath() + if _, err := os.Stat(nssmPath); os.IsNotExist(err) { + errors["NSSM_PATH"] = err + } } return errors diff --git a/local/utl/network/port_pool.go b/local/utl/network/port_pool.go new file mode 100644 index 0000000..c43e58f --- /dev/null +++ b/local/utl/network/port_pool.go @@ -0,0 +1,72 @@ +package network + +import ( + "context" + "fmt" + "os" + "strconv" + "sync" + + "gorm.io/gorm" +) + +const ( + defaultPortPoolMin = 9600 + defaultPortPoolMax = 9700 +) + +type PortPoolManager struct { + mu sync.Mutex + min int + max int + inUse map[int]struct{} +} + +func NewPortPoolManager(db *gorm.DB) (*PortPoolManager, error) { + min := portEnvInt("PORT_POOL_MIN", defaultPortPoolMin) + max := portEnvInt("PORT_POOL_MAX", defaultPortPoolMax) + + var ports []int + if err := db.Raw("SELECT port FROM servers WHERE platform = 'docker' AND port > 0").Scan(&ports).Error; err != nil { + return nil, fmt.Errorf("failed to seed port pool from database: %v", err) + } + + inUse := make(map[int]struct{}, len(ports)) + for _, p := range ports { + inUse[p] = struct{}{} + } + + return &PortPoolManager{ + min: min, + max: max, + inUse: inUse, + }, nil +} + +func (p *PortPoolManager) Acquire(_ context.Context) (int, error) { + p.mu.Lock() + defer p.mu.Unlock() + + for port := p.min; port <= p.max; port++ { + if _, used := p.inUse[port]; !used { + p.inUse[port] = struct{}{} + return port, nil + } + } + return 0, fmt.Errorf("no available ports in pool [%d-%d]", p.min, p.max) +} + +func (p *PortPoolManager) Release(_ context.Context, port int) { + p.mu.Lock() + defer p.mu.Unlock() + delete(p.inUse, port) +} + +func portEnvInt(key string, defaultVal int) int { + if v := os.Getenv(key); v != "" { + if n, err := strconv.Atoi(v); err == nil { + return n + } + } + return defaultVal +} diff --git a/local/utl/server/server.go b/local/utl/server/server.go index 1571486..d5da435 100755 --- a/local/utl/server/server.go +++ b/local/utl/server/server.go @@ -44,7 +44,7 @@ func Start(di *dig.Container) *fiber.App { app.Use(cors.New(cors.Config{ AllowOrigins: allowedOrigin, - AllowHeaders: "Origin, Content-Type, Accept, Authorization, Access-Token", + AllowHeaders: "Origin, Content-Type, Accept, Authorization, Access-Key", AllowMethods: "GET, POST, PUT, DELETE, OPTIONS", AllowCredentials: true, MaxAge: 86400, // 24 hours