Handle settings

This commit is contained in:
nemunaire 2022-10-05 20:16:53 +02:00
parent 8f64a349ec
commit 25a4d7be2c
7 changed files with 210 additions and 58 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ actions
gongs
reveil
routines
settings.json
tracks
vendor
ui/build

View File

@ -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)
})
}

View File

@ -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()

View File

@ -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

54
model/settings.go Normal file
View 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
View 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);
}
}

View File

@ -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;
</script>
<Container class="flex-fill d-flex flex-column py-2">
@ -16,51 +26,75 @@
Paramètres
</h2>
<Form>
<FormGroup>
<Label for="gongIntervals">Intervalle entre les gongs</Label>
<InputGroup>
{#await settingsP}
<div class="d-flex justify-content-center align-items-center gap-2">
<Spinner color="primary" /> Chargement en cours&hellip;
</div>
{:then}
<FormGroup>
<Label for="gongIntervals">Intervalle entre les gongs</Label>
<InputGroup>
<Input
type="number"
id="gongIntervals"
placeholder="20"
bind:value={settings.gong_interval}
on:change={() => settings.save()}
/>
<InputGroupText>min</InputGroupText>
</InputGroup>
</FormGroup>
<FormGroup>
<Label for="weatherDelay">Annonce météo après</Label>
<InputGroup>
<Input
type="number"
id="weatherDelay"
placeholder="5"
bind:value={settings.weather_delay}
on:change={() => settings.save()}
/>
<InputGroupText>min</InputGroupText>
</InputGroup>
</FormGroup>
<FormGroup>
<Label for="weatherRituel">Rituel pour l'annonce météo</Label>
{#if $actions.list}
<Input
type="select"
id="weatherRituel"
bind:value={settings.weather_action}
on:change={() => settings.save()}
>
{#each $actions.list as action (action.id)}
<option value="{action.path}">{action.name}</option>
{/each}
</Input>
{:else}
{#await actions.refresh()}
<div class="d-flex justify-content-center align-items-center gap-2">
<Spinner color="primary" /> Chargement en cours&hellip;
</div>
{/await}
{/if}
</FormGroup>
<FormGroup>
<Label for="greetingLanguage">Langue de salutation</Label>
<Input
type="number"
id="gongIntervals"
placeholder="20"
/>
<InputGroupText>min</InputGroupText>
</InputGroup>
</FormGroup>
<FormGroup>
<Label for="weatherDelay">Annonce météo après</Label>
<InputGroup>
<Input
type="number"
id="weatherDelay"
placeholder="5"
/>
<InputGroupText>min</InputGroupText>
</InputGroup>
</FormGroup>
<FormGroup>
<Label for="weatherRituel">Rituel pour l'annonce météo</Label>
<Input
type="select"
id="weatherRituel"
>
<option value="1">test</option>
</Input>
</FormGroup>
<FormGroup>
<Label for="greetingLanguage">Langue de salutation</Label>
<Input
type="select"
id="greetingLanguage"
>
<option value="fr_FR">Français</option>
<option value="en_US">Anglais</option>
<option value="es_ES">Espagnol</option>
<option value="de_DE">Allemand</option>
</Input>
</FormGroup>
type="select"
id="greetingLanguage"
bind:value={settings.lang}
on:change={() => settings.save()}
>
<option value="fr_FR">Français</option>
<option value="en_US">Anglais</option>
<option value="es_ES">Espagnol</option>
<option value="de_DE">Allemand</option>
</Input>
</FormGroup>
{/await}
</Form>
</Container>