From 9fa5a378e143a4c0db422749da3a46daf35ced81 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 25 Jul 2024 19:38:02 +0200 Subject: [PATCH] Do federation wakeup/stop --- api/alarm.go | 20 +++++++++++++++++- api/alarms.go | 2 +- api/federation.go | 50 ++++++++++++++++++++++++++++++++++++++++++++ api/routes.go | 1 + app.go | 4 ++-- model/alarm.go | 13 +++++++----- player/federation.go | 35 +++++++++++++++++++++++++++++++ player/player.go | 23 +++++++++++++++++++- ui/nojs.go | 4 ++-- 9 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 api/federation.go create mode 100644 player/federation.go diff --git a/api/alarm.go b/api/alarm.go index 23179d4..9bc0dac 100644 --- a/api/alarm.go +++ b/api/alarm.go @@ -1,11 +1,14 @@ package api import ( + "fmt" + "log" "net/http" "github.com/gin-gonic/gin" "git.nemunai.re/nemunaire/reveil/config" + "git.nemunai.re/nemunaire/reveil/model" "git.nemunai.re/nemunaire/reveil/player" ) @@ -20,7 +23,7 @@ func declareAlarmRoutes(cfg *config.Config, router *gin.RouterGroup) { router.POST("/alarm/run", func(c *gin.Context) { if player.CommonPlayer == nil { - err := player.WakeUp(cfg, nil) + err := player.WakeUp(cfg, nil, true) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return @@ -51,6 +54,21 @@ func declareAlarmRoutes(cfg *config.Config, router *gin.RouterGroup) { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } + + settings, err := reveil.ReadSettings(cfg.SettingsFile) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Errorf("Unable to read settings: %s", err.Error())}) + return + } + + for k, srv := range settings.Federation { + err = player.FederatedStop(srv) + if err != nil { + log.Printf("Unable to do federated wakeup on %s: %s", k, err.Error()) + } else { + log.Printf("Federated wakeup on %s: launched!", k) + } + } } c.JSON(http.StatusOK, true) diff --git a/api/alarms.go b/api/alarms.go index b571514..b47d99f 100644 --- a/api/alarms.go +++ b/api/alarms.go @@ -13,7 +13,7 @@ import ( func declareAlarmsRoutes(cfg *config.Config, db *reveil.LevelDBStorage, resetTimer func(), router *gin.RouterGroup) { router.GET("/alarms/next", func(c *gin.Context) { - alarm, _, err := reveil.GetNextAlarm(cfg, db) + alarm, _, _, err := reveil.GetNextAlarm(cfg, db) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return diff --git a/api/federation.go b/api/federation.go new file mode 100644 index 0000000..fc80285 --- /dev/null +++ b/api/federation.go @@ -0,0 +1,50 @@ +package api + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + + "git.nemunai.re/nemunaire/reveil/config" + "git.nemunai.re/nemunaire/reveil/player" +) + +func declareFederationRoutes(cfg *config.Config, router *gin.RouterGroup) { + router.POST("/federation/wakeup", func(c *gin.Context) { + var s map[string]interface{} + c.ShouldBind(s) + + if player.CommonPlayer == nil { + var seed int64 + if tmp, ok := s["seed"].(int64); ok { + seed = tmp + } else { + seed := time.Now().Unix() + seed -= seed % 172800 + } + + err := player.WakeUpFromFederation(cfg, seed, nil) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + } else { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Player already running"}) + return + } + + c.JSON(http.StatusOK, true) + }) + router.POST("/federation/wakeok", func(c *gin.Context) { + if player.CommonPlayer != nil { + err := player.CommonPlayer.Stop() + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + } + + c.JSON(http.StatusOK, true) + }) +} diff --git a/api/routes.go b/api/routes.go index 38b3f40..16c155b 100644 --- a/api/routes.go +++ b/api/routes.go @@ -13,6 +13,7 @@ func DeclareRoutes(router *gin.Engine, cfg *config.Config, db *reveil.LevelDBSto declareActionsRoutes(cfg, apiRoutes) declareAlarmRoutes(cfg, apiRoutes) declareAlarmsRoutes(cfg, db, resetTimer, apiRoutes) + declareFederationRoutes(cfg, apiRoutes) declareGongsRoutes(cfg, apiRoutes) declareHistoryRoutes(cfg, apiRoutes) declareQuotesRoutes(cfg, apiRoutes) diff --git a/app.go b/app.go index dbfb2a0..9d1f823 100644 --- a/app.go +++ b/app.go @@ -79,7 +79,7 @@ func (app *App) ResetTimer() { app.nextAlarm = nil } - if na, routines, err := reveil.GetNextAlarm(app.cfg, app.db); err == nil && na != nil { + if na, routines, federated, err := reveil.GetNextAlarm(app.cfg, app.db); err == nil && na != nil { app.nextAlarm = time.AfterFunc(time.Until(*na), func() { app.nextAlarm = nil reveil.RemoveOldAlarmsSingle(app.db) @@ -87,7 +87,7 @@ func (app *App) ResetTimer() { // Rearm timer for the next time app.ResetTimer() - err := player.WakeUp(app.cfg, routines) + err := player.WakeUp(app.cfg, routines, federated) if err != nil { log.Println(err.Error()) return diff --git a/model/alarm.go b/model/alarm.go index 50aefb1..7e8bca4 100644 --- a/model/alarm.go +++ b/model/alarm.go @@ -40,25 +40,27 @@ func (h *Hour) UnmarshalJSON(src []byte) error { return nil } -func GetNextAlarm(cfg *config.Config, db *LevelDBStorage) (*time.Time, []Identifier, error) { +func GetNextAlarm(cfg *config.Config, db *LevelDBStorage) (*time.Time, []Identifier, bool, error) { alarmsRepeated, err := GetAlarmsRepeated(db) if err != nil { - return nil, nil, err + return nil, nil, false, err } var closestAlarm *time.Time var closestAlarmRoutines []Identifier + var closestAlarmFederated bool for _, alarm := range alarmsRepeated { next := alarm.GetNextOccurence(cfg, db) if next != nil && (closestAlarm == nil || closestAlarm.After(*next)) { closestAlarm = next closestAlarmRoutines = alarm.FollowingRoutines + closestAlarmFederated = alarm.EnableFederation } } alarmsSingle, err := GetAlarmsSingle(db) if err != nil { - return nil, nil, err + return nil, nil, false, err } now := time.Now() @@ -66,10 +68,11 @@ func GetNextAlarm(cfg *config.Config, db *LevelDBStorage) (*time.Time, []Identif if closestAlarm == nil || (closestAlarm.After(alarm.Time) && alarm.Time.After(now)) { closestAlarm = &alarm.Time closestAlarmRoutines = alarm.FollowingRoutines + closestAlarmFederated = alarm.EnableFederation } } - return closestAlarm, closestAlarmRoutines, nil + return closestAlarm, closestAlarmRoutines, closestAlarmFederated, nil } func GetNextException(cfg *config.Config, db *LevelDBStorage) (*time.Time, error) { @@ -90,7 +93,7 @@ func GetNextException(cfg *config.Config, db *LevelDBStorage) (*time.Time, error } func DropNextAlarm(cfg *config.Config, db *LevelDBStorage) error { - timenext, _, err := GetNextAlarm(cfg, db) + timenext, _, _, err := GetNextAlarm(cfg, db) if err != nil { return err } diff --git a/player/federation.go b/player/federation.go new file mode 100644 index 0000000..b62fc58 --- /dev/null +++ b/player/federation.go @@ -0,0 +1,35 @@ +package player + +import ( + "bytes" + "encoding/json" + "net/http" + + "git.nemunai.re/nemunaire/reveil/model" +) + +func FederatedWakeUp(srv reveil.FederationSettings, seed int64) error { + req := map[string]interface{}{"seed": seed} + req_enc, err := json.Marshal(req) + if err != nil { + return err + } + + res, err := http.Post(srv.URL+"/api/federation/wakeup", "application/json", bytes.NewBuffer(req_enc)) + if err != nil { + return err + } + res.Body.Close() + + return nil +} + +func FederatedStop(srv reveil.FederationSettings) error { + res, err := http.Post(srv.URL+"/api/federation/wakeok", "application/json", nil) + if err != nil { + return err + } + res.Body.Close() + + return nil +} diff --git a/player/player.go b/player/player.go index dcee619..3d46e8e 100644 --- a/player/player.go +++ b/player/player.go @@ -43,13 +43,34 @@ type Player struct { playedItem int } -func WakeUp(cfg *config.Config, routine []reveil.Identifier) (err error) { +func WakeUp(cfg *config.Config, routine []reveil.Identifier, federated bool) (err error) { if CommonPlayer != nil { return fmt.Errorf("Unable to start the player: a player is already running") } seed := time.Now().Unix() seed -= seed % 172800 + + if federated { + settings, err := reveil.ReadSettings(cfg.SettingsFile) + if err != nil { + return fmt.Errorf("Unable to read settings: %w", err) + } + + for k, srv := range settings.Federation { + err = FederatedWakeUp(srv, seed) + if err != nil { + log.Printf("Unable to do federated wakeup on %s: %s", k, err.Error()) + } else { + log.Printf("Federated wakeup on %s: launched!", k) + } + } + } + + return WakeUpFromFederation(cfg, seed, routine) +} + +func WakeUpFromFederation(cfg *config.Config, seed int64, routine []reveil.Identifier) (err error) { rand.Seed(seed) CommonPlayer, err = NewPlayer(cfg, routine) diff --git a/ui/nojs.go b/ui/nojs.go index d942c27..438868d 100644 --- a/ui/nojs.go +++ b/ui/nojs.go @@ -24,7 +24,7 @@ func DeclareNoJSRoutes(router *gin.Engine, cfg *config.Config, db *reveil.LevelD router.SetHTMLTemplate(templ) router.GET("/nojs.html", func(c *gin.Context) { - alarm, _, err := reveil.GetNextAlarm(cfg, db) + alarm, _, _, err := reveil.GetNextAlarm(cfg, db) if err != nil { c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"errmsg": err.Error()}) return @@ -122,7 +122,7 @@ func DeclareNoJSRoutes(router *gin.Engine, cfg *config.Config, db *reveil.LevelD case "start": if player.CommonPlayer == nil { - err := player.WakeUp(cfg, nil) + err := player.WakeUp(cfg, nil, true) if err != nil { c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"errmsg": err.Error()}) return