diff --git a/server/backend/internal/httpapi/manage/display.go b/server/backend/internal/httpapi/manage/display.go new file mode 100644 index 0000000..8c101ef --- /dev/null +++ b/server/backend/internal/httpapi/manage/display.go @@ -0,0 +1,53 @@ +package manage + +import ( + "encoding/json" + "log/slog" + "net/http" + + "git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier" + "git.az-it.net/az/morz-infoboard/server/backend/internal/store" +) + +// HandleDisplayCommand nimmt {"state":"on"} oder {"state":"off"} entgegen und +// schickt den entsprechenden MQTT-Befehl an den Agent. +func HandleDisplayCommand(screens *store.ScreenStore, notifier *mqttnotifier.Notifier) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + screenSlug := r.PathValue("screenSlug") + screen, err := screens.GetBySlug(r.Context(), screenSlug) + if err != nil { + http.Error(w, "screen not found", http.StatusNotFound) + return + } + if !requireScreenAccess(w, r, screen) { + return + } + + var body struct { + State string `json:"state"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, "invalid JSON", http.StatusBadRequest) + return + } + + var action string + switch body.State { + case "on": + action = "display_on" + case "off": + action = "display_off" + default: + http.Error(w, `state must be "on" or "off"`, http.StatusBadRequest) + return + } + + if err := notifier.SendDisplayCommand(screenSlug, action); err != nil { + slog.Error("send display command", "err", err) + http.Error(w, "failed to send command", http.StatusBadGateway) + return + } + + w.WriteHeader(http.StatusNoContent) + } +} diff --git a/server/backend/internal/httpapi/router.go b/server/backend/internal/httpapi/router.go index 3a33b78..bbbfbe4 100644 --- a/server/backend/internal/httpapi/router.go +++ b/server/backend/internal/httpapi/router.go @@ -67,7 +67,7 @@ func NewRouter(deps RouterDeps) http.Handler { mux.HandleFunc("GET /api/v1/meta", handleMeta) // ── Player status (existing) ────────────────────────────────────────── - mux.HandleFunc("POST /api/v1/player/status", handlePlayerStatus(deps.StatusStore, deps.Config.MQTTBroker, deps.Config.MQTTUsername, deps.Config.MQTTPassword)) + mux.HandleFunc("POST /api/v1/player/status", handlePlayerStatus(deps.StatusStore, deps.ScreenStore, deps.Config.MQTTBroker, deps.Config.MQTTUsername, deps.Config.MQTTPassword)) mux.HandleFunc("POST /api/v1/player/screenshot", handlePlayerScreenshot(deps.ScreenshotStore)) mux.HandleFunc("GET /api/v1/screens/status", handleListLatestPlayerStatuses(deps.StatusStore)) mux.HandleFunc("GET /api/v1/screens/{screenId}/status", handleGetLatestPlayerStatus(deps.StatusStore)) @@ -183,6 +183,10 @@ func registerManageRoutes(mux *http.ServeMux, d RouterDeps) { mux.Handle("GET /api/v1/screens/{screenId}/screenshot", authOnly(http.HandlerFunc(handleGetScreenshot(d.ScreenshotStore)))) + // ── Display control ─────────────────────────────────────────────────── + mux.Handle("POST /api/v1/screens/{screenSlug}/display", + authOnly(http.HandlerFunc(manage.HandleDisplayCommand(d.ScreenStore, notifier)))) + // ── JSON API — screens ──────────────────────────────────────────────── // Self-registration: no auth (player calls this on startup). mux.HandleFunc("POST /api/v1/screens/register",