add state history

This commit is contained in:
Fran Jurmanović
2025-05-27 20:18:58 +02:00
parent e52894c663
commit 56ef5e1484
12 changed files with 173 additions and 27 deletions

View File

@@ -25,11 +25,13 @@ func Init(di *dig.Container, app *fiber.App) {
},
})
serverIdGroup := groups.Group("/server/:id")
routeGroups := &common.RouteGroups{
Api: groups.Group("/api"),
Server: groups.Group("/server"),
Config: groups.Group("/server/:id").Group("/config"),
Config: serverIdGroup.Group("/config"),
Lookup: groups.Group("/lookup"),
StateHistory: serverIdGroup.Group("/state-history"),
}
groups.Use(basicAuthConfig)

View File

@@ -40,6 +40,11 @@ func InitializeControllers(c *dig.Container) {
if err != nil {
log.Panic("unable to initialize lookup controller")
}
err = c.Invoke(NewStateHistoryController)
if err != nil {
log.Panic("unable to initialize stateHistory controller")
}
}
// FilteredResponse

View File

@@ -0,0 +1,43 @@
package controller
import (
"acc-server-manager/local/service"
"acc-server-manager/local/utl/common"
"github.com/gofiber/fiber/v2"
)
type StateHistoryController struct {
service *service.StateHistoryService
}
// NewStateHistoryController
// Initializes StateHistoryController.
//
// Args:
// *services.StateHistoryService: StateHistory service
// *Fiber.RouterGroup: Fiber Router Group
// Returns:
// *StateHistoryController: Controller for "StateHistory" interactions
func NewStateHistoryController(as *service.StateHistoryService, routeGroups *common.RouteGroups) *StateHistoryController {
ac := &StateHistoryController{
service: as,
}
routeGroups.StateHistory.Get("/", ac.getAll)
return ac
}
// getAll returns StateHistorys
//
// @Summary Return StateHistorys
// @Description Return StateHistorys
// @Tags StateHistory
// @Success 200 {array} string
// @Router /v1/StateHistory [get]
func (ac *StateHistoryController) getAll(c *fiber.Ctx) error {
StateHistoryID, _ := c.ParamsInt("id")
StateHistoryModel := ac.service.GetAll(c, StateHistoryID)
return c.JSON(StateHistoryModel)
}

View File

@@ -31,11 +31,6 @@ type PlayerState struct {
IsConnected bool
}
type AccServerInstance struct {
Model *Server
State *ServerState
}
type State struct {
Session string `json:"session"`
SessionStart time.Time `json:"sessionStart"`
@@ -54,3 +49,11 @@ type ServerState struct {
// Players map[int]*PlayerState
// etc.
}
type StateHistory struct {
ID uint `gorm:"primaryKey" json:"id"`
ServerID uint `json:"serverId" gorm:"not null"`
Session string `json:"session"`
PlayerCount int `json:"playerCount"`
DateCreated time.Time `json:"dateCreated"`
}

View File

@@ -11,6 +11,7 @@ import (
// *dig.Container: Dig Container
func InitializeRepositories(c *dig.Container) {
c.Provide(NewApiRepository)
c.Provide(NewStateHistoryRepository)
c.Provide(NewServerRepository)
c.Provide(NewConfigRepository)
c.Provide(NewLookupRepository)

View File

@@ -0,0 +1,45 @@
package repository
import (
"acc-server-manager/local/model"
"context"
"gorm.io/gorm"
)
type StateHistoryRepository struct {
db *gorm.DB
}
func NewStateHistoryRepository(db *gorm.DB) *StateHistoryRepository {
return &StateHistoryRepository{
db: db,
}
}
// GetAll
// Gets All rows from Server table.
//
// Args:
// context.Context: Application context
// Returns:
// model.ServerModel: Server object from database.
func (as StateHistoryRepository) GetAll(ctx context.Context, id int) *[]model.StateHistory {
db := as.db.WithContext(ctx)
ServerModel := new([]model.StateHistory)
db.Find(&ServerModel).Where("ID = ?", id)
return ServerModel
}
// UpdateServer
// Updates Server row from Server table.
//
// Args:
// context.Context: Application context
// Returns:
// model.Server: Server object from database.
func (as StateHistoryRepository) Insert(ctx context.Context, body *model.StateHistory) *model.StateHistory {
db := as.db.WithContext(ctx)
db.Save(body)
return body
}

View File

@@ -8,22 +8,25 @@ import (
"log"
"path/filepath"
"sync"
"time"
"github.com/gofiber/fiber/v2"
)
type ServerService struct {
repository *repository.ServerRepository
stateHistoryRepo *repository.StateHistoryRepository
apiService *ApiService
instances sync.Map
configService *ConfigService
}
func NewServerService(repository *repository.ServerRepository, apiService *ApiService, configService *ConfigService) *ServerService {
func NewServerService(repository *repository.ServerRepository, stateHistoryRepo *repository.StateHistoryRepository, apiService *ApiService, configService *ConfigService) *ServerService {
service := &ServerService{
repository: repository,
apiService: apiService,
configService: configService,
stateHistoryRepo: stateHistoryRepo,
}
servers := repository.GetAll(context.Background())
for _, server := range *servers {
@@ -40,10 +43,13 @@ func NewServerService(repository *repository.ServerRepository, apiService *ApiSe
func (s *ServerService) StartAccServerRuntime(server *model.Server) {
s.instances.Delete(server.ID)
instance := tracking.NewAccServerInstance(server, func(states ...tracking.StateChange) {
for _, state := range states {
log.Println(tracking.StateChanges[state])
}
instance := tracking.NewAccServerInstance(server, func(state *model.ServerState, states ...tracking.StateChange) {
s.stateHistoryRepo.Insert(context.Background(), &model.StateHistory{
ServerID: server.ID,
Session: state.Session,
PlayerCount: state.PlayerCount,
DateCreated: time.Now().UTC(),
})
})
config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath)
cfg := config.(model.Configuration)

View File

@@ -16,6 +16,7 @@ func InitializeServices(c *dig.Container) {
repository.InitializeRepositories(c)
c.Provide(NewServerService)
c.Provide(NewStateHistoryService)
c.Provide(NewApiService)
c.Provide(NewConfigService)
c.Provide(NewLookupService)

View File

@@ -0,0 +1,29 @@
package service
import (
"acc-server-manager/local/model"
"acc-server-manager/local/repository"
"github.com/gofiber/fiber/v2"
)
type StateHistoryService struct {
repository *repository.StateHistoryRepository
}
func NewStateHistoryService(repository *repository.StateHistoryRepository) *StateHistoryService {
return &StateHistoryService{
repository: repository,
}
}
// GetAll
// Gets All rows from StateHistory table.
//
// Args:
// context.Context: Application context
// Returns:
// string: Application version
func (as StateHistoryService) GetAll(ctx *fiber.Ctx, id int) *[]model.StateHistory {
return as.repository.GetAll(ctx.UserContext(), id)
}

View File

@@ -19,6 +19,7 @@ type RouteGroups struct {
Server fiber.Router
Config fiber.Router
Lookup fiber.Router
StateHistory fiber.Router
}
func CheckError(err error) {

View File

@@ -55,6 +55,10 @@ func Migrate(db *gorm.DB) {
if err != nil {
panic("failed to migrate model.SessionType")
}
err = db.AutoMigrate(&model.StateHistory{})
if err != nil {
panic("failed to migrate model.StateHistory")
}
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
Seed(db)

View File

@@ -24,10 +24,10 @@ var StateChanges = map[StateChange]string {
type AccServerInstance struct {
Model *model.Server
State *model.ServerState
OnStateChange func(...StateChange)
OnStateChange func(*model.ServerState, ...StateChange)
}
func NewAccServerInstance(server *model.Server, onStateChange func(...StateChange)) *AccServerInstance {
func NewAccServerInstance(server *model.Server, onStateChange func(*model.ServerState, ...StateChange)) *AccServerInstance {
return &AccServerInstance{
Model: server,
State: &model.ServerState{PlayerCount: 0},
@@ -109,8 +109,8 @@ var logStateContain = map[LogStateType]string {
var sessionChangeRegex = NewRegexHandler(`Session changed: (\w+) -> (\w+)`, logStateContain[SessionChange])
var leaderboardUpdateRegex = NewRegexHandler(`Updated leaderboard for (\d+) clients`, logStateContain[LeaderboardUpdate])
var udpCountRegex = NewRegexHandler(`Udp message count ((\d+) client`, logStateContain[UDPCount])
var clientsOnlineRegex = NewRegexHandler(`(\d+) client(s) online`, logStateContain[ClientsOnline])
var udpCountRegex = NewRegexHandler(`Udp message count (\d+) client`, logStateContain[UDPCount])
var clientsOnlineRegex = NewRegexHandler(`(\d+) client\(s\) online`, logStateContain[ClientsOnline])
var logStateRegex = map[LogStateType]*StateRegexHandler {
SessionChange: sessionChangeRegex,
@@ -136,39 +136,45 @@ func (instance *AccServerInstance) HandleLogLine(line string) {
}
}
func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState)) {
func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState, changes *[]StateChange)) {
state := instance.State
changes := []StateChange{}
state.Lock()
defer state.Unlock()
callback(state)
callback(state, &changes)
if (len(changes) > 0) {
instance.OnStateChange(state, changes...)
}
}
func (instance *AccServerInstance) UpdatePlayerCount(count int) {
changes := []StateChange{}
instance.UpdateState(func (state *model.ServerState) {
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
if (count == state.PlayerCount) {
return
}
if (count > 0 && state.PlayerCount == 0) {
state.SessionStart = time.Now()
changes = append(changes, Session)
*changes = append(*changes, Session)
} else if (count == 0) {
state.SessionStart = time.Time{}
changes = append(changes, Session)
*changes = append(*changes, Session)
}
state.PlayerCount = count
changes = append(changes, PlayerCount)
*changes = append(*changes, PlayerCount)
})
instance.OnStateChange(changes...)
}
func (instance *AccServerInstance) UpdateSessionChange(session string) {
changes := []StateChange{}
instance.UpdateState(func (state *model.ServerState) {
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
if (session == state.Session) {
return
}
if (state.PlayerCount > 0) {
state.SessionStart = time.Now()
} else {
state.SessionStart = time.Time{}
}
state.Session = session
changes = append(changes, Session)
*changes = append(*changes, Session)
})
instance.OnStateChange(changes...)
}