From 26a0990014408eee55c22ef177dcbf5e73b466a1 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Thu, 13 Jun 2019 21:56:13 -0400 Subject: [PATCH] Save config via Apper interface from Admin dash Ref T613 --- admin.go | 32 ++++++------ cmd/writefreely/main.go | 2 +- handle.go | 111 +++++++++++++++++++++++++++------------- routes.go | 24 ++++----- 4 files changed, 104 insertions(+), 65 deletions(-) diff --git a/admin.go b/admin.go index b2eab1f..696d4eb 100644 --- a/admin.go +++ b/admin.go @@ -347,33 +347,33 @@ func handleAdminUpdateSite(app *App, u *User, w http.ResponseWriter, r *http.Req return impart.HTTPError{http.StatusFound, "/admin/page/" + id + m} } -func handleAdminUpdateConfig(app *App, u *User, w http.ResponseWriter, r *http.Request) error { - app.cfg.App.SiteName = r.FormValue("site_name") - app.cfg.App.SiteDesc = r.FormValue("site_desc") - app.cfg.App.OpenRegistration = r.FormValue("open_registration") == "on" +func handleAdminUpdateConfig(apper Apper, u *User, w http.ResponseWriter, r *http.Request) error { + apper.App().cfg.App.SiteName = r.FormValue("site_name") + apper.App().cfg.App.SiteDesc = r.FormValue("site_desc") + apper.App().cfg.App.OpenRegistration = r.FormValue("open_registration") == "on" mul, err := strconv.Atoi(r.FormValue("min_username_len")) if err == nil { - app.cfg.App.MinUsernameLen = mul + apper.App().cfg.App.MinUsernameLen = mul } mb, err := strconv.Atoi(r.FormValue("max_blogs")) if err == nil { - app.cfg.App.MaxBlogs = mb + apper.App().cfg.App.MaxBlogs = mb } - app.cfg.App.Federation = r.FormValue("federation") == "on" - app.cfg.App.PublicStats = r.FormValue("public_stats") == "on" - app.cfg.App.Private = r.FormValue("private") == "on" - app.cfg.App.LocalTimeline = r.FormValue("local_timeline") == "on" - if app.cfg.App.LocalTimeline && app.timeline == nil { + apper.App().cfg.App.Federation = r.FormValue("federation") == "on" + apper.App().cfg.App.PublicStats = r.FormValue("public_stats") == "on" + apper.App().cfg.App.Private = r.FormValue("private") == "on" + apper.App().cfg.App.LocalTimeline = r.FormValue("local_timeline") == "on" + if apper.App().cfg.App.LocalTimeline && apper.App().timeline == nil { log.Info("Initializing local timeline...") - initLocalTimeline(app) + initLocalTimeline(apper.App()) } - app.cfg.App.UserInvites = r.FormValue("user_invites") - if app.cfg.App.UserInvites == "none" { - app.cfg.App.UserInvites = "" + apper.App().cfg.App.UserInvites = r.FormValue("user_invites") + if apper.App().cfg.App.UserInvites == "none" { + apper.App().cfg.App.UserInvites = "" } m := "?cm=Configuration+saved." - err = config.Save(app.cfg, app.cfgFile) + err = apper.SaveConfig(apper.App().cfg) if err != nil { m = "?cm=" + err.Error() } diff --git a/cmd/writefreely/main.go b/cmd/writefreely/main.go index 1ddb3da..484135d 100644 --- a/cmd/writefreely/main.go +++ b/cmd/writefreely/main.go @@ -118,7 +118,7 @@ func main() { // Set app routes r := mux.NewRouter() - app.InitRoutes(r) + writefreely.InitRoutes(app, r) app.InitStaticRoutes(r) // Serve the application diff --git a/handle.go b/handle.go index acde1a1..81a4823 100644 --- a/handle.go +++ b/handle.go @@ -1,5 +1,5 @@ /* - * Copyright © 2018 A Bunch Tell LLC. + * Copyright © 2018-2019 A Bunch Tell LLC. * * This file is part of WriteFreely. * @@ -36,16 +36,17 @@ const ( ) type ( - handlerFunc func(app *App, w http.ResponseWriter, r *http.Request) error - userHandlerFunc func(app *App, u *User, w http.ResponseWriter, r *http.Request) error - dataHandlerFunc func(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error) - authFunc func(app *App, r *http.Request) (*User, error) + handlerFunc func(app *App, w http.ResponseWriter, r *http.Request) error + userHandlerFunc func(app *App, u *User, w http.ResponseWriter, r *http.Request) error + userApperHandlerFunc func(apper Apper, u *User, w http.ResponseWriter, r *http.Request) error + dataHandlerFunc func(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error) + authFunc func(app *App, r *http.Request) (*User, error) ) type Handler struct { errors *ErrorPages sessionStore *sessions.CookieStore - app *App + app Apper } // ErrorPages hold template HTML error pages for displaying errors to the user. @@ -59,7 +60,7 @@ type ErrorPages struct { // NewHandler returns a new Handler instance, using the given StaticPage data, // and saving alias to the application's CookieStore. -func NewHandler(app *App) *Handler { +func NewHandler(apper Apper) *Handler { h := &Handler{ errors: &ErrorPages{ NotFound: template.Must(template.New("").Parse("{{define \"base\"}}404

Not found.

{{end}}")), @@ -67,8 +68,8 @@ func NewHandler(app *App) *Handler { InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}500

Internal server error.

{{end}}")), Blank: template.Must(template.New("").Parse("{{define \"base\"}}{{.Title}}

{{.Content}}

{{end}}")), }, - sessionStore: app.sessionStore, - app: app, + sessionStore: apper.App().sessionStore, + app: apper, } return h @@ -76,8 +77,8 @@ func NewHandler(app *App) *Handler { // NewWFHandler returns a new Handler instance, using WriteFreely template files. // You MUST call writefreely.InitTemplates() before this. -func NewWFHandler(app *App) *Handler { - h := NewHandler(app) +func NewWFHandler(apper Apper) *Handler { + h := NewHandler(apper) h.SetErrorPages(&ErrorPages{ NotFound: pages["404-general.tmpl"], Gone: pages["410.tmpl"], @@ -104,21 +105,21 @@ func (h *Handler) User(f userHandlerFunc) http.HandlerFunc { defer func() { if e := recover(); e != nil { log.Error("%s: %s", e, debug.Stack()) - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = http.StatusInternalServerError } log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()) }() - u := getUserSession(h.app, r) + u := getUserSession(h.app.App(), r) if u == nil { err := ErrNotLoggedIn status = err.Status return err } - err := f(h.app, u, w, r) + err := f(h.app.App(), u, w, r) if err == nil { status = http.StatusOK } else if err, ok := err.(impart.HTTPError); ok { @@ -142,14 +143,52 @@ func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc { defer func() { if e := recover(); e != nil { log.Error("%s: %s", e, debug.Stack()) - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = http.StatusInternalServerError } log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())) }() - u := getUserSession(h.app, r) + u := getUserSession(h.app.App(), r) + if u == nil || !u.IsAdmin() { + err := impart.HTTPError{http.StatusNotFound, ""} + status = err.Status + return err + } + + err := f(h.app.App(), u, w, r) + if err == nil { + status = http.StatusOK + } else if err, ok := err.(impart.HTTPError); ok { + status = err.Status + } else { + status = http.StatusInternalServerError + } + + return err + }()) + } +} + +// AdminApper handles requests on /admin routes that require an Apper. +func (h *Handler) AdminApper(f userApperHandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + h.handleHTTPError(w, r, func() error { + var status int + start := time.Now() + + defer func() { + if e := recover(); e != nil { + log.Error("%s: %s", e, debug.Stack()) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) + status = http.StatusInternalServerError + } + + log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())) + }() + + u := getUserSession(h.app.App(), r) if u == nil || !u.IsAdmin() { err := impart.HTTPError{http.StatusNotFound, ""} status = err.Status @@ -204,7 +243,7 @@ func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerF log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()) }() - u, err := a(h.app, r) + u, err := a(h.app.App(), r) if err != nil { if err, ok := err.(impart.HTTPError); ok { status = err.Status @@ -214,7 +253,7 @@ func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerF return err } - err = f(h.app, u, w, r) + err = f(h.app.App(), u, w, r) if err == nil { status = 200 } else if err, ok := err.(impart.HTTPError); ok { @@ -277,13 +316,13 @@ func (h *Handler) WebErrors(f handlerFunc, ul UserLevel) http.HandlerFunc { defer func() { if e := recover(); e != nil { - u := getUserSession(h.app, r) + u := getUserSession(h.app.App(), r) username := "None" if u != nil { username = u.Username } log.Error("User: %s\n\n%s: %s", username, e, debug.Stack()) - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } @@ -315,13 +354,13 @@ func (h *Handler) WebErrors(f handlerFunc, ul UserLevel) http.HandlerFunc { } // TODO: pass User object to function - err = f(h.app, w, r) + err = f(h.app.App(), w, r) if err == nil { status = 200 } else if httpErr, ok := err.(impart.HTTPError); ok { status = httpErr.Status if status < 300 || status > 399 { - addSessionFlash(h.app, w, r, httpErr.Message, session) + addSessionFlash(h.app.App(), w, r, httpErr.Message, session) return impart.HTTPError{http.StatusFound, r.Referer()} } } else { @@ -332,7 +371,7 @@ func (h *Handler) WebErrors(f handlerFunc, ul UserLevel) http.HandlerFunc { log.Error(e) } log.Info("Web handler internal error render") - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } @@ -351,14 +390,14 @@ func (h *Handler) Web(f handlerFunc, ul UserLevel) http.HandlerFunc { defer func() { if e := recover(); e != nil { - u := getUserSession(h.app, r) + u := getUserSession(h.app.App(), r) username := "None" if u != nil { username = u.Username } log.Error("User: %s\n\n%s: %s", username, e, debug.Stack()) log.Info("Web deferred internal error render") - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } @@ -388,7 +427,7 @@ func (h *Handler) Web(f handlerFunc, ul UserLevel) http.HandlerFunc { } // TODO: pass User object to function - err := f(h.app, w, r) + err := f(h.app.App(), w, r) if err == nil { status = 200 } else if httpErr, ok := err.(impart.HTTPError); ok { @@ -397,7 +436,7 @@ func (h *Handler) Web(f handlerFunc, ul UserLevel) http.HandlerFunc { e := fmt.Sprintf("[Web handler] 500: %v", err) log.Error(e) log.Info("Web internal error render") - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } @@ -425,7 +464,7 @@ func (h *Handler) All(f handlerFunc) http.HandlerFunc { // TODO: do any needed authentication - err := f(h.app, w, r) + err := f(h.app.App(), w, r) if err != nil { if err, ok := err.(impart.HTTPError); ok { status = err.Status @@ -447,14 +486,14 @@ func (h *Handler) Download(f dataHandlerFunc, ul UserLevel) http.HandlerFunc { defer func() { if e := recover(); e != nil { log.Error("%s: %s", e, debug.Stack()) - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()) }() - data, filename, err := f(h.app, w, r) + data, filename, err := f(h.app.App(), w, r) if err != nil { if err, ok := err.(impart.HTTPError); ok { status = err.Status @@ -543,7 +582,7 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er page.StaticPage Content *template.HTML }{ - StaticPage: pageForReq(h.app, r), + StaticPage: pageForReq(h.app.App(), r), } if err.Message != "" { co := template.HTML(err.Message) @@ -553,12 +592,12 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er return } else if err.Status == http.StatusNotFound { w.WriteHeader(err.Status) - h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) return } else if err.Status == http.StatusInternalServerError { w.WriteHeader(err.Status) log.Info("handleHTTPErorr internal error render") - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) return } else if err.Status == http.StatusAccepted { impart.WriteSuccess(w, "", err.Status) @@ -569,7 +608,7 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er Title string Content template.HTML }{ - pageForReq(h.app, r), + pageForReq(h.app.App(), r), fmt.Sprintf("Uh oh (%d)", err.Status), template.HTML(fmt.Sprintf("

%s

", err.Message)), } @@ -604,7 +643,7 @@ func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."}) return } - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) } func correctPageFromLoginAttempt(r *http.Request) string { @@ -626,7 +665,7 @@ func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc { defer func() { if e := recover(); e != nil { log.Error("Handler.LogHandlerFunc\n\n%s: %s", e, debug.Stack()) - h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app, r)) + h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r)) status = 500 } diff --git a/routes.go b/routes.go index a136970..13dd3a5 100644 --- a/routes.go +++ b/routes.go @@ -31,13 +31,13 @@ func (app *App) InitStaticRoutes(r *mux.Router) { } // InitRoutes adds dynamic routes for the given mux.Router. -func (app *App) InitRoutes(r *mux.Router) *mux.Router { +func InitRoutes(apper Apper, r *mux.Router) *mux.Router { // Create handler - handler := NewWFHandler(app) + handler := NewWFHandler(apper) // Set up routes - hostSubroute := app.cfg.App.Host[strings.Index(app.cfg.App.Host, "://")+3:] - if app.cfg.App.SingleUser { + hostSubroute := apper.App().cfg.App.Host[strings.Index(apper.App().cfg.App.Host, "://")+3:] + if apper.App().cfg.App.SingleUser { hostSubroute = "{domain}" } else { if strings.HasPrefix(hostSubroute, "localhost") { @@ -45,7 +45,7 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { } } - if app.cfg.App.SingleUser { + if apper.App().cfg.App.SingleUser { log.Info("Adding %s routes (single user)...", hostSubroute) } else { log.Info("Adding %s routes (multi-user)...", hostSubroute) @@ -55,7 +55,7 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { write := r.PathPrefix("/").Subrouter() // Federation endpoint configurations - wf := webfinger.Default(wfResolver{app.db, app.cfg}) + wf := webfinger.Default(wfResolver{apper.App().db, apper.App().cfg}) wf.NoTLSHandler = nil // Federation endpoints @@ -64,15 +64,15 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { // webfinger write.HandleFunc(webfinger.WebFingerPath, handler.LogHandlerFunc(http.HandlerFunc(wf.Webfinger))) // nodeinfo - niCfg := nodeInfoConfig(app.db, app.cfg) - ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{app.cfg, app.db}) + niCfg := nodeInfoConfig(apper.App().db, apper.App().cfg) + ni := nodeinfo.NewService(*niCfg, nodeInfoResolver{apper.App().cfg, apper.App().db}) write.HandleFunc(nodeinfo.NodeInfoPath, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfoDiscover))) write.HandleFunc(niCfg.InfoURL, handler.LogHandlerFunc(http.HandlerFunc(ni.NodeInfo))) // Set up dyamic page handlers // Handle auth auth := write.PathPrefix("/api/auth/").Subrouter() - if app.cfg.App.OpenRegistration { + if apper.App().cfg.App.OpenRegistration { auth.HandleFunc("/signup", handler.All(apiSignup)).Methods("POST") } auth.HandleFunc("/login", handler.All(login)).Methods("POST") @@ -145,7 +145,7 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET") write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET") write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET") - write.HandleFunc("/admin/update/config", handler.Admin(handleAdminUpdateConfig)).Methods("POST") + write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST") write.HandleFunc("/admin/update/{page}", handler.Admin(handleAdminUpdateSite)).Methods("POST") // Handle special pages first @@ -159,7 +159,7 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { RouteRead(handler, readPerm, write.PathPrefix("/read").Subrouter()) draftEditPrefix := "" - if app.cfg.App.SingleUser { + if apper.App().cfg.App.SingleUser { draftEditPrefix = "/d" write.HandleFunc("/me/new", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET") } else { @@ -170,7 +170,7 @@ func (app *App) InitRoutes(r *mux.Router) *mux.Router { write.HandleFunc(draftEditPrefix+"/{action}/edit", handler.Web(handleViewPad, UserLevelOptional)).Methods("GET") write.HandleFunc(draftEditPrefix+"/{action}/meta", handler.Web(handleViewMeta, UserLevelOptional)).Methods("GET") // Collections - if app.cfg.App.SingleUser { + if apper.App().cfg.App.SingleUser { RouteCollections(handler, write.PathPrefix("/").Subrouter()) } else { write.HandleFunc("/{prefix:[@~$!\\-+]}{collection}", handler.Web(handleViewCollection, UserLevelOptional))