feat(api): POST /api/v1/screens/{slug}/schedule + Scheduler verdrahtet
ScheduleStore in RouterDeps, HandleUpdateSchedule-Handler, Scheduler-Goroutine in app.Run(), ScreenStore.GetByID hinzugefügt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9b766f9086
commit
83af005fad
3 changed files with 81 additions and 20 deletions
|
|
@ -17,16 +17,19 @@ import (
|
|||
"git.az-it.net/az/morz-infoboard/server/backend/internal/db"
|
||||
"git.az-it.net/az/morz-infoboard/server/backend/internal/httpapi"
|
||||
"git.az-it.net/az/morz-infoboard/server/backend/internal/mqttnotifier"
|
||||
"git.az-it.net/az/morz-infoboard/server/backend/internal/scheduler"
|
||||
"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Config config.Config
|
||||
server *http.Server
|
||||
notifier *mqttnotifier.Notifier
|
||||
authStore *store.AuthStore
|
||||
dbPool *db.Pool // V7: für db.Close() im Shutdown
|
||||
logger *log.Logger
|
||||
Config config.Config
|
||||
server *http.Server
|
||||
notifier *mqttnotifier.Notifier
|
||||
authStore *store.AuthStore
|
||||
scheduleStore *store.ScreenScheduleStore
|
||||
screenStore *store.ScreenStore
|
||||
dbPool *db.Pool // V7: für db.Close() im Shutdown
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func New() (*App, error) {
|
||||
|
|
@ -58,6 +61,7 @@ func New() (*App, error) {
|
|||
media := store.NewMediaStore(pool.Pool)
|
||||
playlists := store.NewPlaylistStore(pool.Pool)
|
||||
authStore := store.NewAuthStore(pool.Pool)
|
||||
schedules := store.NewScreenScheduleStore(pool.Pool)
|
||||
|
||||
// Ensure admin user exists — generate a random password if none is configured.
|
||||
adminPassword := cfg.AdminPassword
|
||||
|
|
@ -96,18 +100,21 @@ func New() (*App, error) {
|
|||
AuthStore: authStore,
|
||||
Notifier: notifier,
|
||||
ScreenshotStore: ss,
|
||||
ScheduleStore: schedules,
|
||||
Config: cfg,
|
||||
UploadDir: cfg.UploadDir,
|
||||
Logger: logger,
|
||||
})
|
||||
|
||||
return &App{
|
||||
Config: cfg,
|
||||
server: &http.Server{Addr: cfg.HTTPAddress, Handler: handler},
|
||||
notifier: notifier,
|
||||
authStore: authStore,
|
||||
dbPool: pool, // V7: Referenz für Shutdown
|
||||
logger: logger,
|
||||
Config: cfg,
|
||||
server: &http.Server{Addr: cfg.HTTPAddress, Handler: handler},
|
||||
notifier: notifier,
|
||||
authStore: authStore,
|
||||
scheduleStore: schedules,
|
||||
screenStore: screens,
|
||||
dbPool: pool, // V7: Referenz für Shutdown
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +144,9 @@ func (a *App) Run() error {
|
|||
}
|
||||
}()
|
||||
|
||||
// Display-Zeitplan-Scheduler
|
||||
go scheduler.Run(ctx, a.scheduleStore, a.screenStore, a.notifier)
|
||||
|
||||
// W2: Signal-Handler für Graceful Shutdown.
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
|
|
|||
46
server/backend/internal/httpapi/manage/schedule.go
Normal file
46
server/backend/internal/httpapi/manage/schedule.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package manage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"git.az-it.net/az/morz-infoboard/server/backend/internal/store"
|
||||
)
|
||||
|
||||
// HandleUpdateSchedule speichert den Zeitplan für ein Display.
|
||||
// Body: {"schedule_enabled":true,"power_on_time":"06:00","power_off_time":"22:00"}
|
||||
func HandleUpdateSchedule(screens *store.ScreenStore, schedules *store.ScreenScheduleStore) 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 {
|
||||
ScheduleEnabled bool `json:"schedule_enabled"`
|
||||
PowerOnTime string `json:"power_on_time"`
|
||||
PowerOffTime string `json:"power_off_time"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
http.Error(w, "invalid JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := schedules.Upsert(r.Context(), &store.ScreenSchedule{
|
||||
ScreenID: screen.ID,
|
||||
ScheduleEnabled: body.ScheduleEnabled,
|
||||
PowerOnTime: body.PowerOnTime,
|
||||
PowerOffTime: body.PowerOffTime,
|
||||
}); err != nil {
|
||||
http.Error(w, "db error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,17 +13,18 @@ import (
|
|||
|
||||
// RouterDeps holds all dependencies needed to build the HTTP router.
|
||||
type RouterDeps struct {
|
||||
StatusStore playerStatusStore
|
||||
TenantStore *store.TenantStore
|
||||
ScreenStore *store.ScreenStore
|
||||
MediaStore *store.MediaStore
|
||||
PlaylistStore *store.PlaylistStore
|
||||
AuthStore *store.AuthStore
|
||||
StatusStore playerStatusStore
|
||||
TenantStore *store.TenantStore
|
||||
ScreenStore *store.ScreenStore
|
||||
MediaStore *store.MediaStore
|
||||
PlaylistStore *store.PlaylistStore
|
||||
AuthStore *store.AuthStore
|
||||
Notifier *mqttnotifier.Notifier
|
||||
ScreenshotStore *ScreenshotStore
|
||||
ScheduleStore *store.ScreenScheduleStore
|
||||
Config config.Config
|
||||
UploadDir string
|
||||
Logger *log.Logger
|
||||
UploadDir string
|
||||
Logger *log.Logger
|
||||
}
|
||||
|
||||
func NewRouter(deps RouterDeps) http.Handler {
|
||||
|
|
@ -187,6 +188,10 @@ func registerManageRoutes(mux *http.ServeMux, d RouterDeps) {
|
|||
mux.Handle("POST /api/v1/screens/{screenSlug}/display",
|
||||
authScreen(http.HandlerFunc(manage.HandleDisplayCommand(notifier))))
|
||||
|
||||
// ── Schedule control ──────────────────────────────────────────────────
|
||||
mux.Handle("POST /api/v1/screens/{screenSlug}/schedule",
|
||||
authScreen(http.HandlerFunc(manage.HandleUpdateSchedule(d.ScreenStore, d.ScheduleStore))))
|
||||
|
||||
// ── JSON API — screens ────────────────────────────────────────────────
|
||||
// Self-registration: no auth (player calls this on startup).
|
||||
mux.HandleFunc("POST /api/v1/screens/register",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue