Handle settings
This commit is contained in:
parent
8f64a349ec
commit
25a4d7be2c
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ actions
|
|||||||
gongs
|
gongs
|
||||||
reveil
|
reveil
|
||||||
routines
|
routines
|
||||||
|
settings.json
|
||||||
tracks
|
tracks
|
||||||
vendor
|
vendor
|
||||||
ui/build
|
ui/build
|
||||||
|
@ -1,16 +1,38 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"git.nemunai.re/nemunaire/reveil/config"
|
"git.nemunai.re/nemunaire/reveil/config"
|
||||||
|
"git.nemunai.re/nemunaire/reveil/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func declareSettingsRoutes(cfg *config.Config, router *gin.RouterGroup) {
|
func declareSettingsRoutes(cfg *config.Config, router *gin.RouterGroup) {
|
||||||
router.GET("/settings", func(c *gin.Context) {
|
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) {
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ func (c *Config) declareFlags() {
|
|||||||
flag.StringVar(&c.BaseURL, "baseurl", c.BaseURL, "URL prepended to each URL")
|
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.Bind, "bind", c.Bind, "Bind port/socket")
|
||||||
flag.StringVar(&c.DevProxy, "dev", c.DevProxy, "Use ui directory instead of embedded assets")
|
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.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.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")
|
flag.StringVar(&c.ActionsDir, "actions-dir", c.ActionsDir, "Path to the directory containing the actions")
|
||||||
@ -22,6 +23,7 @@ func Consolidated() (cfg *Config, err error) {
|
|||||||
// Define defaults options
|
// Define defaults options
|
||||||
cfg = &Config{
|
cfg = &Config{
|
||||||
Bind: "127.0.0.1:8080",
|
Bind: "127.0.0.1:8080",
|
||||||
|
SettingsFile: "./settings.json",
|
||||||
TracksDir: "./tracks/",
|
TracksDir: "./tracks/",
|
||||||
GongsDir: "./gongs/",
|
GongsDir: "./gongs/",
|
||||||
ActionsDir: "./actions/",
|
ActionsDir: "./actions/",
|
||||||
|
@ -10,6 +10,7 @@ type Config struct {
|
|||||||
Bind string
|
Bind string
|
||||||
ExternalURL URL
|
ExternalURL URL
|
||||||
BaseURL string
|
BaseURL string
|
||||||
|
SettingsFile string
|
||||||
TracksDir string
|
TracksDir string
|
||||||
GongsDir string
|
GongsDir string
|
||||||
ActionsDir string
|
ActionsDir string
|
||||||
|
54
model/settings.go
Normal file
54
model/settings.go
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
38
ui/src/lib/settings.js
Normal file
38
ui/src/lib/settings.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,17 @@
|
|||||||
InputGroup,
|
InputGroup,
|
||||||
InputGroupText,
|
InputGroupText,
|
||||||
Label,
|
Label,
|
||||||
|
Spinner,
|
||||||
} from 'sveltestrap';
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { actions } from '../../stores/actions';
|
||||||
|
import { getSettings } from '../../lib/settings';
|
||||||
|
|
||||||
|
let settingsP = getSettings();
|
||||||
|
|
||||||
|
$: settingsP.then((s) => settings = s);
|
||||||
|
|
||||||
|
let settings;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Container class="flex-fill d-flex flex-column py-2">
|
<Container class="flex-fill d-flex flex-column py-2">
|
||||||
@ -16,6 +26,11 @@
|
|||||||
Paramètres
|
Paramètres
|
||||||
</h2>
|
</h2>
|
||||||
<Form>
|
<Form>
|
||||||
|
{#await settingsP}
|
||||||
|
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||||
|
<Spinner color="primary" /> Chargement en cours…
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Label for="gongIntervals">Intervalle entre les gongs</Label>
|
<Label for="gongIntervals">Intervalle entre les gongs</Label>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
@ -23,6 +38,8 @@
|
|||||||
type="number"
|
type="number"
|
||||||
id="gongIntervals"
|
id="gongIntervals"
|
||||||
placeholder="20"
|
placeholder="20"
|
||||||
|
bind:value={settings.gong_interval}
|
||||||
|
on:change={() => settings.save()}
|
||||||
/>
|
/>
|
||||||
<InputGroupText>min</InputGroupText>
|
<InputGroupText>min</InputGroupText>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
@ -35,6 +52,8 @@
|
|||||||
type="number"
|
type="number"
|
||||||
id="weatherDelay"
|
id="weatherDelay"
|
||||||
placeholder="5"
|
placeholder="5"
|
||||||
|
bind:value={settings.weather_delay}
|
||||||
|
on:change={() => settings.save()}
|
||||||
/>
|
/>
|
||||||
<InputGroupText>min</InputGroupText>
|
<InputGroupText>min</InputGroupText>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
@ -42,12 +61,24 @@
|
|||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Label for="weatherRituel">Rituel pour l'annonce météo</Label>
|
<Label for="weatherRituel">Rituel pour l'annonce météo</Label>
|
||||||
|
{#if $actions.list}
|
||||||
<Input
|
<Input
|
||||||
type="select"
|
type="select"
|
||||||
id="weatherRituel"
|
id="weatherRituel"
|
||||||
|
bind:value={settings.weather_action}
|
||||||
|
on:change={() => settings.save()}
|
||||||
>
|
>
|
||||||
<option value="1">test</option>
|
{#each $actions.list as action (action.id)}
|
||||||
|
<option value="{action.path}">{action.name}</option>
|
||||||
|
{/each}
|
||||||
</Input>
|
</Input>
|
||||||
|
{:else}
|
||||||
|
{#await actions.refresh()}
|
||||||
|
<div class="d-flex justify-content-center align-items-center gap-2">
|
||||||
|
<Spinner color="primary" /> Chargement en cours…
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
@ -55,6 +86,8 @@
|
|||||||
<Input
|
<Input
|
||||||
type="select"
|
type="select"
|
||||||
id="greetingLanguage"
|
id="greetingLanguage"
|
||||||
|
bind:value={settings.lang}
|
||||||
|
on:change={() => settings.save()}
|
||||||
>
|
>
|
||||||
<option value="fr_FR">Français</option>
|
<option value="fr_FR">Français</option>
|
||||||
<option value="en_US">Anglais</option>
|
<option value="en_US">Anglais</option>
|
||||||
@ -62,5 +95,6 @@
|
|||||||
<option value="de_DE">Allemand</option>
|
<option value="de_DE">Allemand</option>
|
||||||
</Input>
|
</Input>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/await}
|
||||||
</Form>
|
</Form>
|
||||||
</Container>
|
</Container>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user