morz-infoboard/server/backend/internal/httpapi/playerstatus.go
Jesko Anschütz 896eade0fb Halte letzten Player-Status im Backend
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-22 18:19:17 +01:00

100 lines
3 KiB
Go

package httpapi
import (
"net/http"
"strings"
"time"
)
type playerStatusRequest struct {
ScreenID string `json:"screen_id"`
Timestamp string `json:"ts"`
Status string `json:"status"`
ServerURL string `json:"server_url"`
MQTTBroker string `json:"mqtt_broker"`
HeartbeatEverySeconds int `json:"heartbeat_every_seconds"`
StartedAt string `json:"started_at"`
LastHeartbeatAt string `json:"last_heartbeat_at"`
}
func handlePlayerStatus(store playerStatusStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var request playerStatusRequest
if err := decodeJSON(r, &request); err != nil {
writeError(w, http.StatusBadRequest, "invalid_json", "ungueltiger JSON-Body", nil)
return
}
if strings.TrimSpace(request.ScreenID) == "" {
writeError(w, http.StatusBadRequest, "screen_id_required", "screen_id ist erforderlich", nil)
return
}
if strings.TrimSpace(request.Timestamp) == "" {
writeError(w, http.StatusBadRequest, "timestamp_required", "ts ist erforderlich", nil)
return
}
if strings.TrimSpace(request.Status) == "" {
writeError(w, http.StatusBadRequest, "status_required", "status ist erforderlich", nil)
return
}
if err := validateOptionalRFC3339(request.Timestamp); err != nil {
writeError(w, http.StatusBadRequest, "invalid_timestamp", "ts ist kein gueltiger RFC3339-Zeitstempel", nil)
return
}
if err := validateOptionalRFC3339(request.StartedAt); err != nil {
writeError(w, http.StatusBadRequest, "invalid_started_at", "started_at ist kein gueltiger RFC3339-Zeitstempel", nil)
return
}
if err := validateOptionalRFC3339(request.LastHeartbeatAt); err != nil {
writeError(w, http.StatusBadRequest, "invalid_last_heartbeat_at", "last_heartbeat_at ist kein gueltiger RFC3339-Zeitstempel", nil)
return
}
store.Save(playerStatusRecord{
ScreenID: request.ScreenID,
Timestamp: request.Timestamp,
Status: request.Status,
ServerURL: request.ServerURL,
MQTTBroker: request.MQTTBroker,
HeartbeatEverySeconds: request.HeartbeatEverySeconds,
StartedAt: request.StartedAt,
LastHeartbeatAt: request.LastHeartbeatAt,
})
writeJSON(w, http.StatusOK, map[string]string{
"status": "accepted",
})
}
}
func handleGetLatestPlayerStatus(store playerStatusStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
screenID := strings.TrimSpace(r.PathValue("screenId"))
if screenID == "" {
writeError(w, http.StatusBadRequest, "screen_id_required", "screenId ist erforderlich", nil)
return
}
record, ok := store.Get(screenID)
if !ok {
writeError(w, http.StatusNotFound, "screen_status_not_found", "Fuer diesen Screen liegt noch kein Status vor", nil)
return
}
writeJSON(w, http.StatusOK, record)
}
}
func validateOptionalRFC3339(value string) error {
if strings.TrimSpace(value) == "" {
return nil
}
_, err := time.Parse(time.RFC3339, value)
return err
}