diff --git a/.gitignore b/.gitignore index 7839093..f540086 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ actions gongs reveil routines +settings.json tracks vendor ui/build diff --git a/api/settings.go b/api/settings.go index 3aa2f32..7bc0a58 100644 --- a/api/settings.go +++ b/api/settings.go @@ -1,16 +1,38 @@ package api import ( + "net/http" + "github.com/gin-gonic/gin" "git.nemunai.re/nemunaire/reveil/config" + "git.nemunai.re/nemunaire/reveil/model" ) func declareSettingsRoutes(cfg *config.Config, router *gin.RouterGroup) { router.GET("/settings", func(c *gin.Context) { + settings, err := reveil.ReadSettings(cfg.SettingsFile) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + c.JSON(http.StatusOK, settings) }) router.PUT("/settings", func(c *gin.Context) { + var config reveil.Settings + err := c.ShouldBindJSON(&config) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) + return + } + err = reveil.SaveSettings(cfg.SettingsFile, config) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + c.JSON(http.StatusOK, config) }) } diff --git a/config/cli.go b/config/cli.go index 8b4ad88..d9af183 100644 --- a/config/cli.go +++ b/config/cli.go @@ -10,6 +10,7 @@ func (c *Config) declareFlags() { flag.StringVar(&c.BaseURL, "baseurl", c.BaseURL, "URL prepended to each URL") flag.StringVar(&c.Bind, "bind", c.Bind, "Bind port/socket") flag.StringVar(&c.DevProxy, "dev", c.DevProxy, "Use ui directory instead of embedded assets") + flag.StringVar(&c.SettingsFile, "settings-file", c.SettingsFile, "Path to the file containing the settings") flag.StringVar(&c.TracksDir, "tracks-dir", c.TracksDir, "Path to the directory containing the tracks") flag.StringVar(&c.GongsDir, "gongs-dir", c.GongsDir, "Path to the directory containing the gongs") flag.StringVar(&c.ActionsDir, "actions-dir", c.ActionsDir, "Path to the directory containing the actions") @@ -21,11 +22,12 @@ func (c *Config) declareFlags() { func Consolidated() (cfg *Config, err error) { // Define defaults options cfg = &Config{ - Bind: "127.0.0.1:8080", - TracksDir: "./tracks/", - GongsDir: "./gongs/", - ActionsDir: "./actions/", - RoutinesDir: "./routines/", + Bind: "127.0.0.1:8080", + SettingsFile: "./settings.json", + TracksDir: "./tracks/", + GongsDir: "./gongs/", + ActionsDir: "./actions/", + RoutinesDir: "./routines/", } cfg.declareFlags() diff --git a/config/config.go b/config/config.go index a85fb0d..c018280 100644 --- a/config/config.go +++ b/config/config.go @@ -6,14 +6,15 @@ import ( ) type Config struct { - DevProxy string - Bind string - ExternalURL URL - BaseURL string - TracksDir string - GongsDir string - ActionsDir string - RoutinesDir string + DevProxy string + Bind string + ExternalURL URL + BaseURL string + SettingsFile string + TracksDir string + GongsDir string + ActionsDir string + RoutinesDir string } // parseLine treats a config line and place the read value in the variable diff --git a/model/settings.go b/model/settings.go new file mode 100644 index 0000000..4e4c008 --- /dev/null +++ b/model/settings.go @@ -0,0 +1,54 @@ +package reveil + +import ( + "encoding/json" + "os" + "time" +) + +// Settings represents the settings panel. +type Settings struct { + Language string `json:"language"` + GongInterval time.Duration `json:"gong_interval"` + WeatherDelay time.Duration `json:"weather_delay"` + WeatherAction string `json:"weather_action"` +} + +// ExistsSettings checks if the settings file can by found at the given path. +func ExistsSettings(settingsPath string) bool { + _, err := os.Stat(settingsPath) + return !os.IsNotExist(err) +} + +// ReadSettings parses the file at the given location. +func ReadSettings(path string) (*Settings, error) { + var s Settings + if fd, err := os.Open(path); err != nil { + return nil, err + } else { + defer fd.Close() + jdec := json.NewDecoder(fd) + + if err := jdec.Decode(&s); err != nil { + return &s, err + } + + return &s, nil + } +} + +// SaveSettings saves settings at the given location. +func SaveSettings(path string, s interface{}) error { + if fd, err := os.Create(path); err != nil { + return err + } else { + defer fd.Close() + jenc := json.NewEncoder(fd) + + if err := jenc.Encode(s); err != nil { + return err + } + + return nil + } +} diff --git a/ui/src/lib/settings.js b/ui/src/lib/settings.js new file mode 100644 index 0000000..f549587 --- /dev/null +++ b/ui/src/lib/settings.js @@ -0,0 +1,38 @@ +export class Settings { + constructor(res) { + if (res) { + this.update(res); + } + } + + update({ language, gong_interval, weather_delay, weather_action }) { + this.language = language; + this.gong_interval = gong_interval; + this.weather_delay = weather_delay; + this.weather_action = weather_action; + } + + async save() { + const res = await fetch(`api/settings`, { + method: 'PUT', + headers: {'Accept': 'application/json'}, + body: JSON.stringify(this), + }); + if (res.status == 200) { + const data = await res.json(); + this.update(data); + return data; + } else { + throw new Error((await res.json()).errmsg); + } + } +} + +export async function getSettings() { + const res = await fetch(`api/settings`, {headers: {'Accept': 'application/json'}}) + if (res.status == 200) { + return new Settings(await res.json()); + } else { + throw new Error((await res.json()).errmsg); + } +} diff --git a/ui/src/routes/settings/+page.svelte b/ui/src/routes/settings/+page.svelte index b2e9b28..a399883 100644 --- a/ui/src/routes/settings/+page.svelte +++ b/ui/src/routes/settings/+page.svelte @@ -8,7 +8,17 @@ InputGroup, InputGroupText, Label, + Spinner, } from 'sveltestrap'; + + import { actions } from '../../stores/actions'; + import { getSettings } from '../../lib/settings'; + + let settingsP = getSettings(); + + $: settingsP.then((s) => settings = s); + + let settings; @@ -16,51 +26,75 @@ Paramètres
- - - + {#await settingsP} +
+ Chargement en cours… +
+ {:then} + + + + settings.save()} + /> + min + + + + + + + settings.save()} + /> + min + + + + + + {#if $actions.list} + settings.save()} + > + {#each $actions.list as action (action.id)} + + {/each} + + {:else} + {#await actions.refresh()} +
+ Chargement en cours… +
+ {/await} + {/if} +
+ + + - min -
-
- - - - - - min - - - - - - - - - - - - - - - - - - - + type="select" + id="greetingLanguage" + bind:value={settings.lang} + on:change={() => settings.save()} + > + + + + + + + {/await}