diff --git a/.gitignore b/.gitignore
index 3f5d67b..7839093 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
actions
gongs
reveil
+routines
tracks
vendor
ui/build
diff --git a/api/routines.go b/api/routines.go
index 4be776e..16d7c0a 100644
--- a/api/routines.go
+++ b/api/routines.go
@@ -1,37 +1,81 @@
package api
import (
+ "fmt"
"net/http"
"github.com/gin-gonic/gin"
"git.nemunai.re/nemunaire/reveil/config"
+ "git.nemunai.re/nemunaire/reveil/model"
)
func declareRoutinesRoutes(cfg *config.Config, router *gin.RouterGroup) {
router.GET("/routines", func(c *gin.Context) {
+ routines, err := reveil.LoadRoutines(cfg)
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
+ return
+ }
+ c.JSON(http.StatusOK, routines)
})
router.POST("/routines", func(c *gin.Context) {
-
+ c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "TODO"})
})
- routinesRoutes := router.Group("/routines/:gid")
- routinesRoutes.Use(routineHandler)
+ routinesRoutes := router.Group("/routines/:tid")
+ routinesRoutes.Use(func(c *gin.Context) {
+ routines, err := reveil.LoadRoutines(cfg)
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
+ return
+ }
+
+ for _, t := range routines {
+ if t.Id.ToString() == c.Param("tid") {
+ c.Set("routine", t)
+ c.Next()
+ return
+ }
+ }
+
+ c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Routine not found"})
+ })
routinesRoutes.GET("", func(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("routine"))
})
routinesRoutes.PUT("", func(c *gin.Context) {
- c.JSON(http.StatusOK, c.MustGet("routine"))
+ oldroutine := c.MustGet("routine").(*reveil.Routine)
+
+ var routine reveil.Routine
+ if err := c.ShouldBindJSON(&routine); err != nil {
+ c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
+ return
+ }
+
+ if routine.Name != oldroutine.Name {
+ err := oldroutine.Rename(routine.Name)
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to rename the routine: %s", err.Error())})
+ return
+ }
+ }
+
+ // TODO: change actions
+
+ c.JSON(http.StatusOK, oldroutine)
})
routinesRoutes.DELETE("", func(c *gin.Context) {
- c.JSON(http.StatusOK, c.MustGet("routine"))
+ routine := c.MustGet("routine").(*reveil.Routine)
+
+ err := routine.Remove()
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to remove the routine: %s", err.Error())})
+ return
+ }
+
+ c.JSON(http.StatusOK, nil)
})
}
-
-func routineHandler(c *gin.Context) {
- c.Set("routine", nil)
-
- c.Next()
-}
diff --git a/model/routine.go b/model/routine.go
new file mode 100644
index 0000000..86b06a2
--- /dev/null
+++ b/model/routine.go
@@ -0,0 +1,119 @@
+package reveil
+
+import (
+ "crypto/sha512"
+ "io/fs"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "git.nemunai.re/nemunaire/reveil/config"
+)
+
+type RoutineStep struct {
+ Delay uint64 `json:"delay"`
+ Action string `json:"action"`
+ Args []string `json:"args,omitempty"`
+}
+
+type Routine struct {
+ Id Identifier `json:"id"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ Steps []RoutineStep `json:"steps"`
+}
+
+func LoadRoutine(path string, cfg *config.Config) ([]RoutineStep, error) {
+ ds, err := ioutil.ReadDir(path)
+ if err != nil {
+ return nil, err
+ }
+
+ actionsDir, err := filepath.Abs(cfg.ActionsDir)
+ if err != nil {
+ return nil, err
+ }
+
+ var steps []RoutineStep
+
+ for _, f := range ds {
+ fullpath := filepath.Join(path, f.Name())
+ if f.Mode()&os.ModeSymlink == 0 {
+ continue
+ }
+
+ dst, err := os.Readlink(fullpath)
+ if err != nil {
+ return nil, err
+ }
+
+ if adst, err := filepath.Abs(filepath.Join(path, dst)); err == nil {
+ dst = adst
+ }
+
+ args := strings.Split(f.Name(), "_")
+
+ delay, err := strconv.ParseUint(args[0], 10, 64)
+
+ step := RoutineStep{
+ Delay: delay,
+ Action: strings.TrimPrefix(dst, actionsDir+"/"),
+ }
+ if len(args) > 1 {
+ step.Args = args[1:]
+ }
+
+ steps = append(steps, step)
+ }
+
+ return steps, nil
+}
+
+func LoadRoutines(cfg *config.Config) (routines []*Routine, err error) {
+ err = filepath.Walk(cfg.RoutinesDir, func(path string, d fs.FileInfo, err error) error {
+ if d.IsDir() && path != cfg.RoutinesDir {
+ hash := sha512.Sum512([]byte(path))
+
+ // Explore directory
+ steps, err := LoadRoutine(path, cfg)
+ if err != nil {
+ log.Printf("Invalid routine directory (trying to walk through %s): %s", path, err.Error())
+ // Ignore invalid routines
+ return nil
+ }
+
+ routines = append(routines, &Routine{
+ Id: hash[:],
+ Name: d.Name(),
+ Path: path,
+ Steps: steps,
+ })
+ }
+
+ return nil
+ })
+
+ return
+}
+
+func (r *Routine) Rename(newName string) error {
+ newPath := filepath.Join(filepath.Dir(r.Path), newName)
+
+ err := os.Rename(
+ r.Path,
+ newPath,
+ )
+ if err != nil {
+ return err
+ }
+
+ r.Path = newPath
+ return nil
+}
+
+func (a *Routine) Remove() error {
+ return os.Remove(a.Path)
+}
diff --git a/ui/src/components/CardRoutine.svelte b/ui/src/components/CardRoutine.svelte
index 872ddf5..b0c252d 100644
--- a/ui/src/components/CardRoutine.svelte
+++ b/ui/src/components/CardRoutine.svelte
@@ -1,7 +1,9 @@
@@ -49,11 +37,26 @@
>