package httpapi import ( "sort" "sync" "time" ) type playerStatusRecord struct { ScreenID string `json:"screen_id"` Timestamp string `json:"ts"` Status string `json:"status"` ServerConnectivity string `json:"server_connectivity,omitempty"` ReceivedAt string `json:"received_at,omitempty"` Stale bool `json:"stale,omitempty"` DerivedState string `json:"derived_state,omitempty"` ServerURL string `json:"server_url,omitempty"` MQTTBroker string `json:"mqtt_broker,omitempty"` HeartbeatEverySeconds int `json:"heartbeat_every_seconds,omitempty"` StartedAt string `json:"started_at,omitempty"` LastHeartbeatAt string `json:"last_heartbeat_at,omitempty"` } type playerStatusStore interface { Save(record playerStatusRecord) Get(screenID string) (playerStatusRecord, bool) List() []playerStatusRecord Now() time.Time Delete(screenID string) bool } type inMemoryPlayerStatusStore struct { mu sync.RWMutex records map[string]playerStatusRecord now func() time.Time } func newInMemoryPlayerStatusStore() *inMemoryPlayerStatusStore { return &inMemoryPlayerStatusStore{records: make(map[string]playerStatusRecord), now: time.Now} } func NewPlayerStatusStore() playerStatusStore { return newInMemoryPlayerStatusStore() } func (s *inMemoryPlayerStatusStore) Save(record playerStatusRecord) { s.mu.Lock() defer s.mu.Unlock() if s.now != nil && record.ReceivedAt == "" { record.ReceivedAt = s.now().Format(time.RFC3339) } s.records[record.ScreenID] = record } func (s *inMemoryPlayerStatusStore) Get(screenID string) (playerStatusRecord, bool) { s.mu.RLock() defer s.mu.RUnlock() record, ok := s.records[screenID] return record, ok } func (s *inMemoryPlayerStatusStore) List() []playerStatusRecord { s.mu.RLock() defer s.mu.RUnlock() records := make([]playerStatusRecord, 0, len(s.records)) for _, record := range s.records { records = append(records, record) } sort.Slice(records, func(i, j int) bool { return records[i].ScreenID < records[j].ScreenID }) return records } func (s *inMemoryPlayerStatusStore) Delete(screenID string) bool { s.mu.Lock() defer s.mu.Unlock() _, ok := s.records[screenID] if ok { delete(s.records, screenID) } return ok } func (s *inMemoryPlayerStatusStore) Now() time.Time { if s.now == nil { return time.Now() } return s.now() }