From 2453b598def94649cc7ce9af7155bfaad40cce12 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 24 Jul 2026 15:01:03 +0200 Subject: web: /settings page to edit config + books.toml, persisted and applied live; v0.14.0 --- internal/web/server.go | 193 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 187 insertions(+), 6 deletions(-) (limited to 'internal/web/server.go') diff --git a/internal/web/server.go b/internal/web/server.go index 7c90e9e..34371b7 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -12,11 +12,13 @@ import ( "io/fs" "net" "net/http" + "os" "os/exec" "regexp" "runtime" "strconv" "strings" + "sync" "time" "github.com/lukaszkasprzak/lectio/internal/bible" @@ -32,18 +34,40 @@ import ( // every visitor sees the same five choices regardless of their config file. var bibleVersions = []string{"bt", "wuj", "vul", "grb", "drb"} +// server holds the live, mutable config + book table so /settings can apply +// changes to the running process. All handlers read a snapshot via get()/table(). +type server struct { + mu sync.RWMutex + cfg config.Config + tbl *bible.BookTable +} + +func (s *server) get() config.Config { s.mu.RLock(); defer s.mu.RUnlock(); return s.cfg } +func (s *server) table() *bible.BookTable { s.mu.RLock(); defer s.mu.RUnlock(); return s.tbl } +func (s *server) apply(cfg config.Config, tbl *bible.BookTable) { + s.mu.Lock() + defer s.mu.Unlock() + s.cfg = cfg + if tbl != nil { + s.tbl = tbl + } +} + // NewServer builds lectio-web's route tree. cfg supplies the defaults // (lectionary, versions, theme, offline) that requests can override via -// query parameters; it is never mutated. +// query parameters; it is never mutated -- /settings applies changes to a +// live, mutable copy held by server, which every handler reads per request. func NewServer(cfg config.Config) http.Handler { tbl, _ := bible.LoadBookTable(config.UserBooksTOML()) + s := &server{cfg: cfg, tbl: tbl} mux := http.NewServeMux() - - mux.HandleFunc("GET /{$}", indexHandler(cfg)) - mux.HandleFunc("GET /readings", readingsHandler(cfg)) - mux.HandleFunc("GET /reader", readerHandler(cfg, tbl)) - mux.HandleFunc("GET /theme.css", themeCSSHandler(cfg)) + mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { indexHandler(s.get())(w, r) }) + mux.HandleFunc("GET /readings", func(w http.ResponseWriter, r *http.Request) { readingsHandler(s.get())(w, r) }) + mux.HandleFunc("GET /reader", func(w http.ResponseWriter, r *http.Request) { readerHandler(s.get(), s.table())(w, r) }) + mux.HandleFunc("GET /theme.css", func(w http.ResponseWriter, r *http.Request) { themeCSSHandler(s.get())(w, r) }) + mux.HandleFunc("GET /settings", settingsGet(s)) + mux.HandleFunc("POST /settings", settingsPost(s)) staticSub, err := fs.Sub(staticFS, "static") if err != nil { @@ -508,6 +532,163 @@ func themeCSSHandler(cfg config.Config) http.HandlerFunc { } } +// settingsData drives templates/settings.html. +type settingsData struct { + Cfg config.Config + Books string // current books.toml text (user override, else the embedded default) + ThemeOpts []themeOpt // for the web_theme