first
This commit is contained in:
24
backend/internal/settings/runtime.go
Normal file
24
backend/internal/settings/runtime.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package settings
|
||||
|
||||
import "sync"
|
||||
|
||||
type Runtime struct {
|
||||
mu sync.RWMutex
|
||||
current Settings
|
||||
}
|
||||
|
||||
func NewRuntime(initial Settings) *Runtime {
|
||||
return &Runtime{current: initial}
|
||||
}
|
||||
|
||||
func (r *Runtime) Get() Settings {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.current
|
||||
}
|
||||
|
||||
func (r *Runtime) Update(next Settings) {
|
||||
r.mu.Lock()
|
||||
r.current = next
|
||||
r.mu.Unlock()
|
||||
}
|
||||
148
backend/internal/settings/store.go
Normal file
148
backend/internal/settings/store.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
mu sync.RWMutex
|
||||
path string
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
Theme string `yaml:"theme" json:"theme"`
|
||||
RepoURL string `yaml:"repoUrl" json:"repoUrl"`
|
||||
TTLDays int `yaml:"ttlDays" json:"ttlDays"`
|
||||
MaxPayloadBytes int `yaml:"maxPayloadBytes" json:"maxPayloadBytes"`
|
||||
AutoPurgePayloads bool `yaml:"autoPurgePayloads" json:"autoPurgePayloads"`
|
||||
AutoPurgePayloadBytes int `yaml:"autoPurgePayloadBytes" json:"autoPurgePayloadBytes"`
|
||||
AutoExpandDepth int `yaml:"autoExpandDepth" json:"autoExpandDepth"`
|
||||
ImageDetection bool `yaml:"imageDetectionEnabled" json:"imageDetectionEnabled"`
|
||||
HighlightMs int `yaml:"highlightMs" json:"highlightMs"`
|
||||
MQTTProfiles []MQTTProfile `yaml:"mqttProfiles" json:"mqttProfiles"`
|
||||
ActiveProfileID string `yaml:"activeProfileId" json:"activeProfileId"`
|
||||
ApplyViewFilter bool `yaml:"applyViewFilter" json:"applyViewFilter"`
|
||||
ExpandTreeOnStart bool `yaml:"expandTreeOnStart" json:"expandTreeOnStart"`
|
||||
TopicFilters []TopicFilter `yaml:"topicFilters" json:"topicFilters"`
|
||||
UIFontSize int `yaml:"uiFontSize" json:"uiFontSize"`
|
||||
TopicFontSize int `yaml:"topicFontSize" json:"topicFontSize"`
|
||||
PayloadFontSize int `yaml:"payloadFontSize" json:"payloadFontSize"`
|
||||
StatsRefreshMs int `yaml:"statsRefreshMs" json:"statsRefreshMs"`
|
||||
ResizeHandlePx int `yaml:"resizeHandlePx" json:"resizeHandlePx"`
|
||||
}
|
||||
|
||||
type MQTTProfile struct {
|
||||
ID string `yaml:"id" json:"id"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Host string `yaml:"host" json:"host"`
|
||||
Port int `yaml:"port" json:"port"`
|
||||
Username string `yaml:"username" json:"username"`
|
||||
Password string `yaml:"password" json:"password"`
|
||||
IsDefault bool `yaml:"isDefault" json:"isDefault"`
|
||||
}
|
||||
|
||||
type TopicFilter struct {
|
||||
Topic string `yaml:"topic" json:"topic"`
|
||||
Save bool `yaml:"save" json:"save"`
|
||||
View bool `yaml:"view" json:"view"`
|
||||
}
|
||||
|
||||
func NewStore(path string) *Store {
|
||||
return &Store{path: path}
|
||||
}
|
||||
|
||||
func (s *Store) Load(defaults Settings) (Settings, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
content, err := os.ReadFile(s.path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return defaults, nil
|
||||
}
|
||||
return defaults, fmt.Errorf("lecture settings: %w", err)
|
||||
}
|
||||
|
||||
var loaded Settings
|
||||
if err := yaml.Unmarshal(content, &loaded); err != nil {
|
||||
return defaults, fmt.Errorf("yaml settings: %w", err)
|
||||
}
|
||||
|
||||
return merge(defaults, loaded), nil
|
||||
}
|
||||
|
||||
func (s *Store) Save(next Settings) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
dir := filepath.Dir(s.path)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return fmt.Errorf("mkdir settings: %w", err)
|
||||
}
|
||||
|
||||
content, err := yaml.Marshal(next)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal settings: %w", err)
|
||||
}
|
||||
|
||||
return os.WriteFile(s.path, content, 0o644)
|
||||
}
|
||||
|
||||
func merge(base, override Settings) Settings {
|
||||
merged := base
|
||||
if override.Theme != "" {
|
||||
merged.Theme = override.Theme
|
||||
}
|
||||
if override.RepoURL != "" {
|
||||
merged.RepoURL = override.RepoURL
|
||||
}
|
||||
if override.TTLDays != 0 {
|
||||
merged.TTLDays = override.TTLDays
|
||||
}
|
||||
if override.MaxPayloadBytes != 0 {
|
||||
merged.MaxPayloadBytes = override.MaxPayloadBytes
|
||||
}
|
||||
merged.AutoPurgePayloads = override.AutoPurgePayloads
|
||||
if override.AutoPurgePayloadBytes != 0 {
|
||||
merged.AutoPurgePayloadBytes = override.AutoPurgePayloadBytes
|
||||
}
|
||||
if override.AutoExpandDepth != 0 {
|
||||
merged.AutoExpandDepth = override.AutoExpandDepth
|
||||
}
|
||||
merged.ImageDetection = override.ImageDetection
|
||||
if override.HighlightMs != 0 {
|
||||
merged.HighlightMs = override.HighlightMs
|
||||
}
|
||||
if len(override.MQTTProfiles) > 0 {
|
||||
merged.MQTTProfiles = override.MQTTProfiles
|
||||
}
|
||||
if override.ActiveProfileID != "" {
|
||||
merged.ActiveProfileID = override.ActiveProfileID
|
||||
}
|
||||
merged.ApplyViewFilter = override.ApplyViewFilter
|
||||
merged.ExpandTreeOnStart = override.ExpandTreeOnStart
|
||||
if len(override.TopicFilters) > 0 {
|
||||
merged.TopicFilters = override.TopicFilters
|
||||
}
|
||||
if override.UIFontSize != 0 {
|
||||
merged.UIFontSize = override.UIFontSize
|
||||
}
|
||||
if override.TopicFontSize != 0 {
|
||||
merged.TopicFontSize = override.TopicFontSize
|
||||
}
|
||||
if override.PayloadFontSize != 0 {
|
||||
merged.PayloadFontSize = override.PayloadFontSize
|
||||
}
|
||||
if override.StatsRefreshMs != 0 {
|
||||
merged.StatsRefreshMs = override.StatsRefreshMs
|
||||
}
|
||||
if override.ResizeHandlePx != 0 {
|
||||
merged.ResizeHandlePx = override.ResizeHandlePx
|
||||
}
|
||||
return merged
|
||||
}
|
||||
Reference in New Issue
Block a user