status updates
This commit is contained in:
@@ -69,7 +69,7 @@ func (ac *ApiController) getStatus(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return c.Status(400).SendString(strings.ReplaceAll(err.Error(), "\x00", ""))
|
||||
}
|
||||
return c.SendString(apiModel)
|
||||
return c.SendString(string(apiModel))
|
||||
}
|
||||
|
||||
// startServer starts service
|
||||
|
||||
@@ -58,11 +58,7 @@ func (ac *ConfigController) updateConfig(c *fiber.Ctx) error {
|
||||
return c.Status(400).SendString(err.Error())
|
||||
}
|
||||
if restart {
|
||||
serviceName, err := ac.apiService.GetServiceName(c)
|
||||
if err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "Unable to restart service"})
|
||||
}
|
||||
ac.apiService.RestartServer(serviceName)
|
||||
ac.apiService.ApiRestartServer(c)
|
||||
}
|
||||
|
||||
return c.JSON(ConfigModel)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package model
|
||||
|
||||
type ServiceStatus string
|
||||
|
||||
const (
|
||||
StatusRunning ServiceStatus = "SERVICE_RUNNING\r\n"
|
||||
StatusStopped ServiceStatus = "SERVICE_STOPPED\r\n"
|
||||
StatusRestarting ServiceStatus = "SERVICE_RESTARTING\r\n"
|
||||
)
|
||||
|
||||
type ApiModel struct {
|
||||
Api string `json:"api"`
|
||||
}
|
||||
|
||||
// ServiceStatus represents a Windows service state
|
||||
type ServiceStatus struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"` // "running", "stopped", "pending"
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ type Config struct {
|
||||
}
|
||||
|
||||
type Configurations struct {
|
||||
Configuration map[string]interface{} `json:"configuration"`
|
||||
Entrylist map[string]interface{} `json:"entrylist"`
|
||||
Event map[string]interface{} `json:"event"`
|
||||
EventRules map[string]interface{} `json:"eventRules"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
Configuration Configuration `json:"configuration"`
|
||||
AssistRules AssistRules `json:"assistRules"`
|
||||
Event EventConfig `json:"event"`
|
||||
EventRules EventRules `json:"eventRules"`
|
||||
Settings ServerSettings `json:"settings"`
|
||||
}
|
||||
|
||||
type ServerSettings struct {
|
||||
@@ -64,6 +64,18 @@ type Session struct {
|
||||
SessionDurationMinutes int `json:"sessionDurationMinutes"`
|
||||
}
|
||||
|
||||
type AssistRules struct {
|
||||
StabilityControlLevelMax int `json:"stabilityControlLevelMax"`
|
||||
DisableAutosteer int `json:"disableAutosteer"`
|
||||
DisableAutoLights int `json:"disableAutoLights"`
|
||||
DisableAutoWiper int `json:"disableAutoWiper"`
|
||||
DisableAutoEngineStart int `json:"disableAutoEngineStart"`
|
||||
DisableAutoPitLimiter int `json:"disableAutoPitLimiter"`
|
||||
DisableAutoGear int `json:"disableAutoGear"`
|
||||
DisableAutoClutch int `json:"disableAutoClutch"`
|
||||
DisableIdealLine int `json:"disableIdealLine"`
|
||||
}
|
||||
|
||||
type EventRules struct {
|
||||
QualifyStandingType int `json:"qualifyStandingType"`
|
||||
PitWindowLengthSec int `json:"pitWindowLengthSec"`
|
||||
@@ -77,3 +89,12 @@ type EventRules struct {
|
||||
IsMandatoryPitstopSwapDriverRequired bool `json:"isMandatoryPitstopSwapDriverRequired"`
|
||||
TyreSetCount int `json:"tyreSetCount"`
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
UdpPort int `json:"udpPort"`
|
||||
TcpPort int `json:"tcpPort"`
|
||||
MaxConnections int `json:"maxConnections"`
|
||||
LanDiscovery int `json:"lanDiscovery"`
|
||||
RegisterToLobby int `json:"registerToLobby"`
|
||||
ConfigVersion int `json:"configVersion"`
|
||||
}
|
||||
|
||||
@@ -1,12 +1,42 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Server represents an ACC server instance
|
||||
type Server struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Status string `json:"status"`
|
||||
Status ServiceStatus `json:"status"`
|
||||
IP string `gorm:"not null" json:"-"`
|
||||
Port int `gorm:"not null" json:"-"`
|
||||
ConfigPath string `gorm:"not null" json:"-"` // e.g. "/acc/servers/server1/"
|
||||
ServiceName string `gorm:"not null" json:"-"` // Windows service name
|
||||
BroadcastIP string `json:"-"`
|
||||
BroadcastPort int `json:"-"`
|
||||
BroadcastPassword string `json:"-"`
|
||||
}
|
||||
|
||||
type PlayerState struct {
|
||||
CarID int // Car ID in broadcast packets
|
||||
DriverName string // Optional: pulled from registration packet
|
||||
TeamName string
|
||||
CarModel string
|
||||
CurrentLap int
|
||||
LastLapTime int // in milliseconds
|
||||
BestLapTime int // in milliseconds
|
||||
Position int
|
||||
ConnectedAt time.Time
|
||||
DisconnectedAt *time.Time
|
||||
IsConnected bool
|
||||
}
|
||||
|
||||
type ServerState struct {
|
||||
sync.RWMutex
|
||||
Session string
|
||||
PlayerCount int
|
||||
Players map[int]*PlayerState
|
||||
// etc.
|
||||
}
|
||||
@@ -62,3 +62,22 @@ func (as ServerRepository) GetAll(ctx context.Context) *[]model.Server {
|
||||
db.Find(&ServerModel)
|
||||
return ServerModel
|
||||
}
|
||||
|
||||
// UpdateServer
|
||||
// Updates Server row from Server table.
|
||||
//
|
||||
// Args:
|
||||
// context.Context: Application context
|
||||
// Returns:
|
||||
// model.Server: Server object from database.
|
||||
func (as ServerRepository) UpdateServer(ctx context.Context, body *model.Server) *model.Server {
|
||||
db := as.db.WithContext(ctx)
|
||||
|
||||
existingServer := new(model.Server)
|
||||
result := db.Where("id=?", body.ID).First(existingServer)
|
||||
if !errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
body.ID = existingServer.ID
|
||||
}
|
||||
db.Save(body)
|
||||
return body
|
||||
}
|
||||
|
||||
@@ -17,6 +17,47 @@ import (
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
const (
|
||||
ConfigurationJson = "configuration.json"
|
||||
AssistRulesJson = "assistRules.json"
|
||||
EventJson = "event.json"
|
||||
EventRulesJson = "eventRules.json"
|
||||
SettingsJson = "settings.json"
|
||||
)
|
||||
|
||||
var decodeMap = map[string]func(string) (interface{}, error){
|
||||
ConfigurationJson: func(f string) (interface{}, error) {
|
||||
return readAndDecode[model.Configuration](f, ConfigurationJson)
|
||||
},
|
||||
AssistRulesJson: func(f string) (interface{}, error) {
|
||||
return readAndDecode[model.AssistRules](f, AssistRulesJson)
|
||||
},
|
||||
EventJson: func(f string) (interface{}, error) {
|
||||
return readAndDecode[model.EventConfig](f, EventJson)
|
||||
},
|
||||
EventRulesJson: func(f string) (interface{}, error) {
|
||||
return readAndDecode[model.EventRules](f, EventRulesJson)
|
||||
},
|
||||
SettingsJson: func(f string) (interface{}, error) {
|
||||
return readAndDecode[model.ServerSettings](f, SettingsJson)
|
||||
},
|
||||
}
|
||||
|
||||
func DecodeFileName(fileName string) func(path string) (interface{}, error) {
|
||||
if decoder, ok := decodeMap[fileName]; ok {
|
||||
return decoder
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustDecode[T any](fileName, path string) (T, error) {
|
||||
result, err := DecodeFileName(fileName)(path)
|
||||
if err != nil {
|
||||
var zero T
|
||||
return zero, err
|
||||
}
|
||||
return result.(T), nil
|
||||
}
|
||||
|
||||
type ConfigService struct {
|
||||
repository *repository.ConfigRepository
|
||||
@@ -77,17 +118,32 @@ func (as ConfigService) UpdateConfig(ctx *fiber.Ctx, body *map[string]interface{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
newDataUTF16, err := EncodeUTF16LEBOM(newData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
context := ctx.UserContext()
|
||||
|
||||
if (configFile == ConfigurationJson) {
|
||||
config, err := DecodeToMap[model.Configuration](newDataUTF16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (server.BroadcastPort != config.UdpPort || server.Port != config.TcpPort) {
|
||||
server.BroadcastPort = config.UdpPort
|
||||
server.Port = config.TcpPort
|
||||
as.serverRepository.UpdateServer(context, server)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, newDataUTF16, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Log change
|
||||
return as.repository.UpdateConfig(ctx.UserContext(), &model.Config{
|
||||
return as.repository.UpdateConfig(context, &model.Config{
|
||||
ServerID: uint(serverID),
|
||||
ConfigFile: configFile,
|
||||
OldConfig: string(oldDataUTF8),
|
||||
@@ -103,7 +159,7 @@ func (as ConfigService) UpdateConfig(ctx *fiber.Ctx, body *map[string]interface{
|
||||
// context.Context: Application context
|
||||
// Returns:
|
||||
// string: Application version
|
||||
func (as ConfigService) GetConfig(ctx *fiber.Ctx) (map[string]interface{}, error) {
|
||||
func (as ConfigService) GetConfig(ctx *fiber.Ctx) (interface{}, error) {
|
||||
serverID, _ := ctx.ParamsInt("id")
|
||||
configFile := ctx.Params("file")
|
||||
|
||||
@@ -113,13 +169,7 @@ func (as ConfigService) GetConfig(ctx *fiber.Ctx) (map[string]interface{}, error
|
||||
return nil, fiber.NewError(404, "Server not found")
|
||||
}
|
||||
|
||||
config, err := readFile(server.ConfigPath, configFile)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decoded, err := DecodeToMap(config)
|
||||
decoded, err := DecodeFileName(configFile)(server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -143,47 +193,27 @@ func (as ConfigService) GetConfigs(ctx *fiber.Ctx) (*model.Configurations, error
|
||||
return nil, fiber.NewError(404, "Server not found")
|
||||
}
|
||||
|
||||
configuration, err := readFile(server.ConfigPath, "configuration.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedconfiguration, err := DecodeToMap(configuration)
|
||||
decodedconfiguration, err := mustDecode[model.Configuration](ConfigurationJson, server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entrylist, err := readFile(server.ConfigPath, "entrylist.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedentrylist, err := DecodeToMap(entrylist)
|
||||
decodedAssistRules, err := mustDecode[model.AssistRules](AssistRulesJson, server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
event, err := readFile(server.ConfigPath, "event.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedevent, err := DecodeToMap(event)
|
||||
decodedevent, err := mustDecode[model.EventConfig](EventJson, server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventRules, err := readFile(server.ConfigPath, "eventRules.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedeventRules, err := DecodeToMap(eventRules)
|
||||
decodedeventRules, err := mustDecode[model.EventRules](EventRulesJson, server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settings, err := readFile(server.ConfigPath, "settings.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedsettings, err := DecodeToMap(settings)
|
||||
decodedsettings, err := mustDecode[model.ServerSettings](SettingsJson, server.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -193,10 +223,24 @@ func (as ConfigService) GetConfigs(ctx *fiber.Ctx) (*model.Configurations, error
|
||||
Event: decodedevent,
|
||||
EventRules: decodedeventRules,
|
||||
Settings: decodedsettings,
|
||||
Entrylist: decodedentrylist,
|
||||
AssistRules: decodedAssistRules,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readAndDecode[T interface{}](path string, configFile string) (T, error) {
|
||||
settings, err := readFile(path, configFile)
|
||||
var zero T
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
decodedsettings, err := DecodeToMap[T](settings)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
|
||||
return decodedsettings, nil
|
||||
}
|
||||
|
||||
func readFile(path string, configFile string) ([]byte, error) {
|
||||
configPath := filepath.Join(path, "\\server\\cfg", configFile)
|
||||
oldData, err := os.ReadFile(configPath)
|
||||
@@ -219,19 +263,20 @@ func DecodeUTF16LEBOM(input []byte) ([]byte, error) {
|
||||
return transformBytes(decoder.NewDecoder(), input)
|
||||
}
|
||||
|
||||
func DecodeToMap(input []byte) (map[string]interface{}, error) {
|
||||
func DecodeToMap[T interface{}](input []byte) (T, error) {
|
||||
var zero T
|
||||
if input == nil {
|
||||
return nil, nil
|
||||
return zero, nil
|
||||
}
|
||||
configUTF8 := new(map[string]interface{})
|
||||
configUTF8 := new(T)
|
||||
decoded, err := DecodeUTF16LEBOM(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return zero, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(decoded, configUTF8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return zero, err
|
||||
}
|
||||
return *configUTF8, nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (as ServerService) GetAll(ctx *fiber.Ctx) *[]model.Server {
|
||||
|
||||
for i, server := range *servers {
|
||||
status, _ := as.apiService.StatusServer(server.ServiceName)
|
||||
(*servers)[i].Status = status
|
||||
(*servers)[i].Status = model.ServiceStatus(status)
|
||||
}
|
||||
|
||||
return servers
|
||||
@@ -46,7 +46,8 @@ func (as ServerService) GetAll(ctx *fiber.Ctx) *[]model.Server {
|
||||
// string: Application version
|
||||
func (as ServerService) GetById(ctx *fiber.Ctx, serverID int) *model.Server {
|
||||
server := as.repository.GetFirst(ctx.UserContext(), serverID)
|
||||
server.Status, _ = as.apiService.StatusServer(server.ServiceName);
|
||||
status, _ := as.apiService.StatusServer(server.ServiceName)
|
||||
server.Status = model.ServiceStatus(status)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
func InitializeServices(c *dig.Container) {
|
||||
repository.InitializeRepositories(c)
|
||||
|
||||
c.Provide(NewServerService)
|
||||
c.Provide(NewApiService)
|
||||
c.Provide(NewConfigService)
|
||||
c.Provide(NewServerService)
|
||||
c.Provide(NewLookupService)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user