From 684787bcb72aece2aa914597a3bc8788432e66f7 Mon Sep 17 00:00:00 2001 From: Leonardo Bishop Date: Sun, 13 Jul 2025 21:23:34 +0100 Subject: Add flags --- web/command/handler/flags.go | 71 +++++++++++++++++++++++ web/command/handler/site.go | 5 +- web/command/handler/upload.go | 6 +- web/command/html/error.go | 13 +++++ web/command/html/flags.go | 130 ++++++++++++++++++++++++++++++++++++++++++ web/command/html/home.go | 8 +++ web/command/html/site.go | 9 ++- web/command/html/style.css | 14 ++++- web/mux.go | 2 + 9 files changed, 246 insertions(+), 12 deletions(-) create mode 100644 web/command/handler/flags.go create mode 100644 web/command/html/error.go create mode 100644 web/command/html/flags.go (limited to 'web') diff --git a/web/command/handler/flags.go b/web/command/handler/flags.go new file mode 100644 index 0000000..b218739 --- /dev/null +++ b/web/command/handler/flags.go @@ -0,0 +1,71 @@ +package handler + +import ( + "fmt" + "net/http" + "path" + + "github.com/LMBishop/scrapbook/pkg/config" + "github.com/LMBishop/scrapbook/pkg/index" + "github.com/LMBishop/scrapbook/web/command/html" + . "maragu.dev/gomponents" + ghttp "maragu.dev/gomponents/http" +) + +func GetFlags(index *index.SiteIndex) func(http.ResponseWriter, *http.Request) { + return ghttp.Adapt(func(w http.ResponseWriter, r *http.Request) (Node, error) { + siteName := r.PathValue("site") + if siteName == "" { + return html.ErrorPage("Unknown site: " + siteName), nil + } + site := index.GetSite(siteName) + if site == nil { + return html.ErrorPage("Unknown site: " + siteName), nil + } + + return html.FlagsPage("", "", siteName, site.SiteConfig.Flags), nil + }) +} + +func PostFlags(mainConfig *config.MainConfig, index *index.SiteIndex) func(http.ResponseWriter, *http.Request) { + return ghttp.Adapt(func(w http.ResponseWriter, r *http.Request) (Node, error) { + siteName := r.PathValue("site") + if siteName == "" { + return html.ErrorPage("Unknown site: " + siteName), nil + } + site := index.GetSite(siteName) + if site == nil { + return html.ErrorPage("Unknown site: " + siteName), nil + } + + err := r.ParseForm() + if err != nil { + return html.FlagsPage("", fmt.Errorf("Could not parse form: %w", err).Error(), siteName, site.SiteConfig.Flags), nil + } + + var flags config.SiteFlag + if r.FormValue("disable") == "on" { + flags = flags | config.FlagDisable + } + if r.FormValue("tls") == "on" { + flags = flags | config.FlagTLS + } + if r.FormValue("index") == "on" { + flags = flags | config.FlagIndex + } + if r.FormValue("password") == "on" { + flags = flags | config.FlagPassword + } + if r.FormValue("readonly") == "on" { + flags = flags | config.FlagReadOnly + } + + site.SiteConfig.Flags = flags + err = config.WriteSiteConfig(path.Join(site.Path, "site.toml"), site.SiteConfig) + if err != nil { + return html.FlagsPage("", fmt.Errorf("Failed to persist flags: %w", err).Error(), siteName, flags), nil + } + + return html.FlagsPage(fmt.Sprintf("Successfully set flags %s", site.ConvertFlagsToString()), "", siteName, flags), nil + }) +} diff --git a/web/command/handler/site.go b/web/command/handler/site.go index 73ea1a0..ab1d8e5 100644 --- a/web/command/handler/site.go +++ b/web/command/handler/site.go @@ -1,7 +1,6 @@ package handler import ( - "fmt" "net/http" "github.com/LMBishop/scrapbook/pkg/config" @@ -15,11 +14,11 @@ func GetSite(mainConfig *config.MainConfig, index *index.SiteIndex) func(http.Re return ghttp.Adapt(func(w http.ResponseWriter, r *http.Request) (Node, error) { siteName := r.PathValue("site") if siteName == "" { - return nil, fmt.Errorf("unknown site") + return html.ErrorPage("Unknown site: " + siteName), nil } site := index.GetSite(siteName) if site == nil { - return nil, fmt.Errorf("unknown site") + return html.ErrorPage("Unknown site: " + siteName), nil } return html.SitePage(mainConfig, site), nil diff --git a/web/command/handler/upload.go b/web/command/handler/upload.go index 0e4928c..96bbef1 100644 --- a/web/command/handler/upload.go +++ b/web/command/handler/upload.go @@ -16,11 +16,11 @@ func GetUpload(index *index.SiteIndex) func(http.ResponseWriter, *http.Request) return ghttp.Adapt(func(w http.ResponseWriter, r *http.Request) (Node, error) { siteName := r.PathValue("site") if siteName == "" { - return nil, fmt.Errorf("unknown site") + return html.ErrorPage("Unknown site: " + siteName), nil } site := index.GetSite(siteName) if site == nil { - return nil, fmt.Errorf("unknown site") + return html.ErrorPage("Unknown site: " + siteName), nil } return html.UploadPage("", "", siteName), nil @@ -31,7 +31,7 @@ func PostUpload(mainConfig *config.MainConfig, index *index.SiteIndex) func(http return ghttp.Adapt(func(w http.ResponseWriter, r *http.Request) (Node, error) { siteName := r.PathValue("site") if siteName == "" { - return nil, fmt.Errorf("unknown site") + return html.ErrorPage("Unknown site: " + siteName), nil } reader, err := r.MultipartReader() diff --git a/web/command/html/error.go b/web/command/html/error.go new file mode 100644 index 0000000..b39cc19 --- /dev/null +++ b/web/command/html/error.go @@ -0,0 +1,13 @@ +package html + +import ( + . "maragu.dev/gomponents" +) + +func ErrorPage(err string) Node { + return page("Error", + alertError(err), + + navButton("Home", "/"), + ) +} diff --git a/web/command/html/flags.go b/web/command/html/flags.go new file mode 100644 index 0000000..a7fb99f --- /dev/null +++ b/web/command/html/flags.go @@ -0,0 +1,130 @@ +package html + +import ( + "fmt" + + "github.com/LMBishop/scrapbook/pkg/config" + . "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func FlagsPage(success, err string, siteName string, flags config.SiteFlag) Node { + return page("Set flags for "+siteName, + H1(Text("Set flags for "+siteName)), + + If(success != "", Group{ + alertSuccess(success), + Div( + Class("control-group group-right"), + navButton("OK", fmt.Sprintf("/site/%s/", siteName)), + ), + }), + + If(success == "", Group{ + If(err != "", alertError(err)), + + Form( + Method("post"), + + P(Text("These flags affect the behaviour of scrapbook's internal web server. They will have no effect if you are serving the site using a different web server.")), + + FieldSet( + Legend(Text("Flags")), + Span( + Input( + ID("disable"), + Name("disable"), + Type("checkbox"), + If(flags&config.FlagDisable != 0, Checked()), + ), + Label( + For("disable"), + Text("Disable"), + ), + ), + Span( + Class("form-help"), + Text("Disallow access to this site."), + ), + + Span( + Input( + ID("tls"), + Name("tls"), + Type("checkbox"), + If(flags&config.FlagTLS != 0, Checked()), + ), + Label( + For("tls"), + Text("TLS"), + ), + ), + Span( + Class("form-help"), + Text("Serve this site on the HTTPS socket."), + ), + + Span( + Input( + ID("index"), + Name("index"), + Type("checkbox"), + If(flags&config.FlagIndex != 0, Checked()), + ), + Label( + For("index"), + Text("Automatic index"), + ), + ), + Span( + Class("form-help"), + Text("Generate index.html files on the fly if they do not exist."), + ), + + Span( + Input( + ID("password"), + Name("password"), + Type("checkbox"), + If(flags&config.FlagPassword != 0, Checked()), + ), + Label( + For("password"), + Text("Password protect"), + ), + ), + Span( + Class("form-help"), + Text("Require visitors to enter a password to view the site."), + ), + + Span( + Input( + ID("readonly"), + Name("readonly"), + Type("checkbox"), + If(flags&config.FlagReadOnly != 0, Checked()), + ), + Label( + For("readonly"), + Text("Read only"), + ), + ), + Span( + Class("form-help"), + Text("Disallow new site revisions or modification."), + ), + ), + + Div( + Class("control-group group-right"), + navButton("Go back", fmt.Sprintf("/site/%s/", siteName)), + Input( + Type("submit"), + Value("Submit"), + ), + ), + ), + }), + ) +} diff --git a/web/command/html/home.go b/web/command/html/home.go index f0e783d..490b2b8 100644 --- a/web/command/html/home.go +++ b/web/command/html/home.go @@ -24,6 +24,10 @@ func HomePage(siteIndex *index.SiteIndex) Node { Class("header status"), Text("Status"), ), + Span( + Class("header flags"), + Text("Flags"), + ), Span( Class("header actions"), Text("Actions"), @@ -40,6 +44,10 @@ func HomePage(siteIndex *index.SiteIndex) Node { Class("status"), Text(site.EvaluateSiteStatus()), ), + Span( + Class("flags"), + Text(site.ConvertFlagsToString()), + ), Span( Class("actions"), navButton("Details", fmt.Sprintf("/site/%s/", site.Name)), diff --git a/web/command/html/site.go b/web/command/html/site.go index b4239b1..7616e6b 100644 --- a/web/command/html/site.go +++ b/web/command/html/site.go @@ -25,7 +25,7 @@ func SitePage(mainConfig *config.MainConfig, site *site.Site) Node { Class("control-group"), navButton("Upload new version", "upload"), - navButton("Disable site", "disable"), + navButton("Set flags", "flags"), navButton("Delete site", "delete"), ), ), @@ -65,8 +65,11 @@ func SitePage(mainConfig *config.MainConfig, site *site.Site) Node { ), }), - H2(Text("API endpoints")), - P(Code(Text(fmt.Sprintf("http://%s/api/site/%s/upload", mainConfig.Command.Host, site.Name)))), + H2(Text("Information")), + + P(Text("API endpoint for new versions: "), Code(Text(fmt.Sprintf("POST https://%s/api/site/%s/upload", mainConfig.Command.Host, site.Name)))), + + P(Text("Data directory on system: "), Code(Text(site.Path))), navButton("Go back", "/"), ) diff --git a/web/command/html/style.css b/web/command/html/style.css index 7e40ad4..fbefaa1 100644 --- a/web/command/html/style.css +++ b/web/command/html/style.css @@ -49,10 +49,14 @@ legend { font-style: italic; } -label { +label:not(input[type=checkbox]+label) { font-weight: bold; } +input[type=checkbox] { + margin: 0 0.25rem 0 0; +} + footer { margin-top: 2rem; color: gray; @@ -61,7 +65,7 @@ footer { button, input[type=submit] { background: none!important; - font-family: arial, sans-serif; + font-family: sans-serif; font-size: medium; cursor: pointer; } @@ -98,7 +102,7 @@ a:hover, button:hover, input[type=submit]:hover, button:active, a:active, input[ .sites-table { width: 100%; display: grid; - grid-template-columns: 50% 1fr 1fr; + grid-template-columns: 40% 1fr 1fr 1fr; align-items: center; gap: 1rem; margin-bottom: 1rem; @@ -113,6 +117,10 @@ a:hover, button:hover, input[type=submit]:hover, button:active, a:active, input[ flex-direction: column; } +.sites-table > .flags:not(.header) { + font-family: monospace; +} + .sites-table > .name > span:nth-child(2) { font-size: smaller; } diff --git a/web/mux.go b/web/mux.go index b6a5528..0821723 100644 --- a/web/mux.go +++ b/web/mux.go @@ -16,6 +16,8 @@ func NewMux(cfg *config.MainConfig, siteIndex *index.SiteIndex) *http.ServeMux { mux.HandleFunc("GET /site/{site}/", handler.GetSite(cfg, siteIndex)) mux.HandleFunc("GET /site/{site}/upload", handler.GetUpload(siteIndex)) mux.HandleFunc("POST /site/{site}/upload", handler.PostUpload(cfg, siteIndex)) + mux.HandleFunc("GET /site/{site}/flags", handler.GetFlags(siteIndex)) + mux.HandleFunc("POST /site/{site}/flags", handler.PostFlags(cfg, siteIndex)) return mux } -- cgit v1.2.3-70-g09d2