diff options
| -rw-r--r-- | pkg/index/index.go | 8 | ||||
| -rw-r--r-- | pkg/site/site.go | 8 | ||||
| -rw-r--r-- | web/command/handler/delete.go | 57 | ||||
| -rw-r--r-- | web/command/html/delete.go | 58 | ||||
| -rw-r--r-- | web/command/html/flags.go | 3 | ||||
| -rw-r--r-- | web/command/html/home.go | 19 | ||||
| -rw-r--r-- | web/command/html/site.go | 10 | ||||
| -rw-r--r-- | web/command/html/style.css | 12 | ||||
| -rw-r--r-- | web/mux.go | 2 |
9 files changed, 171 insertions, 6 deletions
diff --git a/pkg/index/index.go b/pkg/index/index.go index f3dc00d..f741708 100644 --- a/pkg/index/index.go +++ b/pkg/index/index.go @@ -55,6 +55,14 @@ func (s *SiteIndex) AddSite(site *site.Site) { s.updateSiteIndexes() } +func (s *SiteIndex) RemoveSite(name string) { + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.sites, name) + s.updateSiteIndexes() +} + func (s *SiteIndex) UpdateSiteIndexes() { s.mu.Lock() defer s.mu.Unlock() diff --git a/pkg/site/site.go b/pkg/site/site.go index 704c34d..82f25ad 100644 --- a/pkg/site/site.go +++ b/pkg/site/site.go @@ -8,6 +8,7 @@ import ( "path" "path/filepath" "regexp" + "slices" "strings" "time" @@ -92,6 +93,10 @@ func (s *Site) UpdateVersion(newVersion string) error { return os.Symlink(newVersion, currentVersionPath) } +func (s *Site) DeleteDataOnDisk() error { + return os.RemoveAll(s.Path) +} + func (s *Site) GetAllVersions() ([]string, error) { entries, err := os.ReadDir(s.Path) @@ -116,6 +121,9 @@ func (s *Site) GetAllVersions() ([]string, error) { } } + slices.Sort(versions) + slices.Reverse(versions) + return versions, err } diff --git a/web/command/handler/delete.go b/web/command/handler/delete.go new file mode 100644 index 0000000..f95e278 --- /dev/null +++ b/web/command/handler/delete.go @@ -0,0 +1,57 @@ +package handler + +import ( + "fmt" + "net/http" + + "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 GetDelete(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.DeletePage("", "", siteName), nil + }) +} + +func PostDelete(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.DeletePage("", fmt.Errorf("Could not parse form: %w", err).Error(), siteName), nil + } + + if r.FormValue("delete") != "on" { + return html.DeletePage("", "You need to check the box to continue", siteName), nil + } + + err = site.DeleteDataOnDisk() + if err != nil { + return html.DeletePage("", fmt.Errorf("Error occurred during data deletion: %w", err).Error(), siteName), nil + } + index.RemoveSite(siteName) + + return html.DeletePage(fmt.Sprintf("Successfully deleted site %s", siteName), "", siteName), nil + }) +} diff --git a/web/command/html/delete.go b/web/command/html/delete.go new file mode 100644 index 0000000..a2059f5 --- /dev/null +++ b/web/command/html/delete.go @@ -0,0 +1,58 @@ +package html + +import ( + "fmt" + + . "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func DeletePage(success, err string, siteName string) Node { + return page("Delete "+siteName, + H1(Text("Delete "+siteName)), + + If(success != "", Group{ + alertSuccess(success), + Div( + Class("control-group group-right"), + navButton("OK", "/"), + ), + }), + + If(success == "", Group{ + If(err != "", alertError(err)), + + Form( + Method("post"), + + FieldSet( + Legend(Text("Delete")), + Span( + Input( + ID("delete"), + Name("delete"), + Type("checkbox"), + ), + Label( + For("delete"), + Text(fmt.Sprintf("Really delete site %s?", siteName)), + ), + ), + Span( + Class("form-help"), + Text("Check the box to confirm deletion. Data on disk (including all site versions) will be deleted. This action is irreversible."), + ), + ), + + 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/flags.go b/web/command/html/flags.go index a7fb99f..728b415 100644 --- a/web/command/html/flags.go +++ b/web/command/html/flags.go @@ -52,6 +52,7 @@ func FlagsPage(success, err string, siteName string, flags config.SiteFlag) Node ID("tls"), Name("tls"), Type("checkbox"), + Disabled(), If(flags&config.FlagTLS != 0, Checked()), ), Label( @@ -69,6 +70,7 @@ func FlagsPage(success, err string, siteName string, flags config.SiteFlag) Node ID("index"), Name("index"), Type("checkbox"), + Disabled(), If(flags&config.FlagIndex != 0, Checked()), ), Label( @@ -86,6 +88,7 @@ func FlagsPage(success, err string, siteName string, flags config.SiteFlag) Node ID("password"), Name("password"), Type("checkbox"), + Disabled(), If(flags&config.FlagPassword != 0, Checked()), ), Label( diff --git a/web/command/html/home.go b/web/command/html/home.go index b9d585c..5dc236a 100644 --- a/web/command/html/home.go +++ b/web/command/html/home.go @@ -33,7 +33,13 @@ func HomePage(siteIndex *index.SiteIndex) Node { Text("Actions"), ), }, + Map(siteIndex.GetSites(), func(site *site.Site) Node { + status := site.EvaluateSiteStatus() + good := false + if status == "live" { + good = true + } return Group{ Span( Class("name"), @@ -42,8 +48,9 @@ func HomePage(siteIndex *index.SiteIndex) Node { If(site.SiteConfig.Host != "", Span(Text(fmt.Sprintf("on %s", site.SiteConfig.Host)))), ), Span( - Class("status"), - Text(site.EvaluateSiteStatus()), + If(good, Class("status text-good")), + If(!good, Class("status text-bad")), + Text(status), ), Span( Class("flags"), @@ -57,6 +64,12 @@ func HomePage(siteIndex *index.SiteIndex) Node { }), ), - navButton("Create new", "/create"), + If(len(siteIndex.GetSites()) == 0, alert("There are no sites to display", "")), + + Div( + Class("control-group group-right"), + + navButton("Create new", "/create"), + ), ) } diff --git a/web/command/html/site.go b/web/command/html/site.go index 7da9dc0..b3e0cc1 100644 --- a/web/command/html/site.go +++ b/web/command/html/site.go @@ -24,9 +24,8 @@ func SitePage(mainConfig *config.MainConfig, site *site.Site) Node { Div( Class("control-group"), - navButton("Upload new version", "upload"), - navButton("Set flags", "flags"), navButton("Change host", "host"), + navButton("Set flags", "flags"), navButton("Delete site", "delete"), ), ), @@ -64,6 +63,11 @@ func SitePage(mainConfig *config.MainConfig, site *site.Site) Node { } }), ), + Div( + Class("control-group group-right"), + + navButton("Upload new version", "upload"), + ), }), H2(Text("Information")), @@ -72,6 +76,8 @@ func SitePage(mainConfig *config.MainConfig, site *site.Site) Node { P(Text("Data directory on system: "), Code(Text(site.Path))), + Br(), + navButton("Go back", "/"), ) } diff --git a/web/command/html/style.css b/web/command/html/style.css index fbefaa1..e8db90c 100644 --- a/web/command/html/style.css +++ b/web/command/html/style.css @@ -126,6 +126,8 @@ a:hover, button:hover, input[type=submit]:hover, button:active, a:active, input[ } .sites-table > .actions, .versions-table > .actions { + display: flex; + gap: 0.5rem; justify-self: flex-end; } @@ -149,7 +151,7 @@ a:hover, button:hover, input[type=submit]:hover, button:active, a:active, input[ } .versions-table > .date > .current { - color: #2ECC40; + color: #0dbe22; font-size: smaller; font-style: italic; } @@ -170,4 +172,12 @@ a:hover, button:hover, input[type=submit]:hover, button:active, a:active, input[ .alert.error { background-color: #ffcccb; border: 1px solid #ff6666; +} + +.text-good { + color: #0dbe22; +} + +.text-bad { + color: #c02020; }
\ No newline at end of file @@ -25,6 +25,8 @@ func NewMux(cfg *config.MainConfig, siteIndex *index.SiteIndex, authenticator *a mux.HandleFunc("POST /site/{site}/flags", middleware.MustAuthenticate(authenticator, handler.PostFlags(cfg, siteIndex))) mux.HandleFunc("GET /site/{site}/host", middleware.MustAuthenticate(authenticator, handler.GetHost(siteIndex))) mux.HandleFunc("POST /site/{site}/host", middleware.MustAuthenticate(authenticator, handler.PostHost(cfg, siteIndex))) + mux.HandleFunc("GET /site/{site}/delete", middleware.MustAuthenticate(authenticator, handler.GetDelete(siteIndex))) + mux.HandleFunc("POST /site/{site}/delete", middleware.MustAuthenticate(authenticator, handler.PostDelete(cfg, siteIndex))) return mux } |
