From 331e768124b19a359d7112c1add8d3abef0b5bdd Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Sat, 6 Dec 2025 23:14:55 -0700 Subject: [PATCH] grouping community and conference routes and making more functionality implemented by middleware --- community.go | 56 ++++------------------------- communityadmin.go | 38 ++++---------------- conference.go | 8 +---- errors.go | 10 ------ main.go | 39 ++++++++++++--------- ui/amcontext.go | 67 +++++++++++++++++------------------ ui/middleware.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++ ui/render_wrap.go | 70 ++++++++++--------------------------- 8 files changed, 175 insertions(+), 202 deletions(-) create mode 100644 ui/middleware.go diff --git a/community.go b/community.go index 9f40ba6..c699f70 100644 --- a/community.go +++ b/community.go @@ -36,12 +36,7 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) { if err != nil { return ui.ErrorPage(ctxt, err) } - err = ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware ci, err := comm.ContactInfo() if err != nil { return ui.ErrorPage(ctxt, err) @@ -132,7 +127,6 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) { ctxt.VarMap().Set("homePage", *ci.URL) } - ctxt.SetLeftMenu("community") ctxt.VarMap().Set("amsterdam_pageTitle", "Community Profile: "+comm.Name) return "framed_template", "comprofile.jet", nil } @@ -147,13 +141,7 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) { */ func JoinCommunity(ctxt ui.AmContext) (string, any, error) { me := ctxt.CurrentUser() - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - ctxt.SetLeftMenu("community") - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware mbr, _, _, err := comm.Membership(me) if err != nil { return ui.ErrorPage(ctxt, err) @@ -193,13 +181,7 @@ func JoinCommunity(ctxt ui.AmContext) (string, any, error) { */ func JoinCommunityWithKey(ctxt ui.AmContext) (string, any, error) { me := ctxt.CurrentUser() - err := ctxt.SetCommunityContext(ctxt.FormField("cc")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() - ctxt.SetLeftMenu("community") + comm := ctxt.CurrentCommunity() // set by middleware mbr, _, _, err := comm.Membership(me) if err != nil { return ui.ErrorPage(ctxt, err) @@ -249,13 +231,7 @@ func JoinCommunityWithKey(ctxt ui.AmContext) (string, any, error) { */ func UnjoinCommunity(ctxt ui.AmContext) (string, any, error) { me := ctxt.CurrentUser() - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() - ctxt.SetLeftMenu("community") + comm := ctxt.CurrentCommunity() // set by middleware mbr, lock, _, err := comm.Membership(me) if err != nil { return ui.ErrorPage(ctxt, err) @@ -283,13 +259,7 @@ func UnjoinCommunity(ctxt ui.AmContext) (string, any, error) { */ func UnjoinCommunityConfirm(ctxt ui.AmContext) (string, any, error) { me := ctxt.CurrentUser() - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() - ctxt.SetLeftMenu("community") + comm := ctxt.CurrentCommunity() // set by middleware mbr, lock, _, err := comm.Membership(me) if err != nil { return ui.ErrorPage(ctxt, err) @@ -325,12 +295,7 @@ func UnjoinCommunityConfirm(ctxt ui.AmContext) (string, any, error) { * Standard Go error status. */ func MemberList(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware ofs := 0 p := ctxt.Parameter("ofs") if p != "" { @@ -339,7 +304,6 @@ func MemberList(ctxt ui.AmContext) (string, any, error) { ofs = v } } - ctxt.SetLeftMenu("community") ctxt.VarMap().Set("comm", comm) ctxt.VarMap().Set("hostUid", *comm.HostUid) showHidden := ctxt.TestPermission("Community.ShowHiddenMembers") @@ -381,17 +345,11 @@ func MemberList(ctxt ui.AmContext) (string, any, error) { * Standard Go error status. */ func MemberSearch(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware ofs, _ := ctxt.FormFieldInt("ofs") field := ctxt.FormField("field") oper := ctxt.FormField("oper") term := ctxt.FormField("term") - ctxt.SetLeftMenu("community") ctxt.VarMap().Set("comm", comm) ctxt.VarMap().Set("hostUid", comm.HostUid) showHidden := ctxt.TestPermission("Community.ShowHiddenMembers") diff --git a/communityadmin.go b/communityadmin.go index 352cb12..c52597f 100644 --- a/communityadmin.go +++ b/communityadmin.go @@ -32,12 +32,7 @@ import ( * Standard Go error status. */ func CommunityAdminMenu(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware if !comm.TestPermission("Community.ShowAdmin", ctxt.EffectiveLevel()) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) @@ -47,7 +42,6 @@ func CommunityAdminMenu(ctxt ui.AmContext) (string, any, error) { if !ctxt.GlobalFlags().Get(database.GlobalFlagNoCategories) { defs["USECAT"] = true } - ctxt.SetLeftMenu("community") ctxt.VarMap().Set("menu", menu.FilterCommunity(comm)) ctxt.VarMap().Set("defs", defs) ctxt.VarMap().Set("amsterdam_pageTitle", menu.Title+" - "+comm.Name) @@ -87,18 +81,13 @@ func communityLogoURL(ci *database.ContactInfo) string { * Standard Go error status. */ func CommunityProfileForm(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) } var ci *database.ContactInfo - ci, err = comm.ContactInfo() + ci, err := comm.ContactInfo() if err != nil { return ui.ErrorPage(ctxt, err) } @@ -170,12 +159,7 @@ func validateJoinKey(dlg *ui.Dialog) error { * Standard Go error status. */ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) @@ -269,12 +253,7 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) { * Standard Go error status. */ func CommunityLogoForm(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) @@ -299,12 +278,7 @@ func CommunityLogoForm(ctxt ui.AmContext) (string, any, error) { * Standard Go error status. */ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) diff --git a/conference.go b/conference.go index 8d0f69b..6b4903a 100644 --- a/conference.go +++ b/conference.go @@ -25,12 +25,7 @@ import ( // conferencesPrequel consolidates some of the basic conference checks into one function. func conferencesPrequel(ctxt ui.AmContext) (string, any, error) { - err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) - if err != nil { - ctxt.SetRC(http.StatusNotFound) - return ui.ErrorPage(ctxt, err) - } - comm := ctxt.CurrentCommunity() + comm := ctxt.CurrentCommunity() // set by middleware b, err := database.AmTestService(comm, "Conference") if err != nil { return ui.ErrorPage(ctxt, err) @@ -47,7 +42,6 @@ func conferencesPrequel(ctxt ui.AmContext) (string, any, error) { ctxt.SetRC(http.StatusForbidden) return ui.ErrorPage(ctxt, errors.New("you are not authorized access to conferences")) } - ctxt.SetLeftMenu("community") return "", nil, nil } diff --git a/errors.go b/errors.go index 529cdca..5d58baf 100644 --- a/errors.go +++ b/errors.go @@ -10,7 +10,6 @@ package main import ( - "fmt" "net/http" "git.erbosoft.com/amy/amsterdam/ui" @@ -49,15 +48,6 @@ func AmErrorHandler(err error, c echo.Context) { } amctxt := ui.AmContextFromEchoContext(c) - if amctxt == nil { - var qerr error - amctxt, qerr = ui.AmCreateContext(c) - if qerr != nil { - log.Errorf("failed to create AmContext in error handler: %v", qerr) - c.String(errCode, fmt.Sprintf("error %d: %v", errCode, err)) - return - } - } amctxt.SetLeftMenu("top") amctxt.SetRC(errCode) amctxt.VarMap().Set("error", err.Error()) diff --git a/main.go b/main.go index b507b2f..bf6e11f 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,9 @@ func setupEcho() *echo.Echo { } e.Use(LogrusMiddleware) e.Use(session.Middleware(ui.SessionStore)) + e.Use(ui.ContextCreator) + e.Use(ui.IPBanTest) + e.Use(ui.CookieLoginTest) fn := ui.AmWrap(NotImplPage) e.GET("/TODO/*", fn) @@ -75,22 +78,26 @@ func setupEcho() *echo.Echo { e.GET("/create_comm", ui.AmWrap(CreateCommunityForm)) e.POST("/create_comm", ui.AmWrap(CreateCommunity)) e.POST("/attachment_upload", ui.AmWrap(AttachmentUpload)) - e.GET("/comm/:cid/profile", ui.AmWrap(ShowCommunity)) - e.GET("/comm/:cid/join", ui.AmWrap(JoinCommunity)) - e.POST("/comm/:cid/join", ui.AmWrap(JoinCommunityWithKey)) - e.GET("/comm/:cid/unjoin", ui.AmWrap(UnjoinCommunity)) - e.POST("/comm/:cid/unjoin", ui.AmWrap(UnjoinCommunityConfirm)) - e.GET("/comm/:cid/members", ui.AmWrap(MemberList)) - e.POST("/comm/:cid/members", ui.AmWrap(MemberSearch)) - e.GET("/comm/:cid/admin", ui.AmWrap(CommunityAdminMenu)) - e.GET("/comm/:cid/admin/profile", ui.AmWrap(CommunityProfileForm)) - e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile)) - e.GET("/comm/:cid/admin/logo", ui.AmWrap(CommunityLogoForm)) - e.POST("/comm/:cid/admin/logo", ui.AmWrap(EditCommunityLogo)) - e.GET("/comm/:cid/conf", ui.AmWrap(Conferences)) - e.GET("/comm/:cid/conf/:confid", ui.AmWrap(Topics)) - e.GET("/comm/:cid/conf/:confid/new_topic", ui.AmWrap(NewTopicForm)) - e.POST("/comm/:cid/conf/:confid/new_topic", ui.AmWrap(NewTopic)) + + commGroup := e.Group("/comm/:cid", ui.SetCommunity) + commGroup.GET("/profile", ui.AmWrap(ShowCommunity)) + commGroup.GET("/join", ui.AmWrap(JoinCommunity)) + commGroup.POST("/join", ui.AmWrap(JoinCommunityWithKey)) + commGroup.GET("/unjoin", ui.AmWrap(UnjoinCommunity)) + commGroup.POST("/unjoin", ui.AmWrap(UnjoinCommunityConfirm)) + commGroup.GET("/members", ui.AmWrap(MemberList)) + commGroup.POST("/members", ui.AmWrap(MemberSearch)) + commGroup.GET("/admin", ui.AmWrap(CommunityAdminMenu)) + commGroup.GET("/admin/profile", ui.AmWrap(CommunityProfileForm)) + commGroup.POST("/admin/profile", ui.AmWrap(EditCommunityProfile)) + commGroup.GET("/admin/logo", ui.AmWrap(CommunityLogoForm)) + commGroup.POST("/admin/logo", ui.AmWrap(EditCommunityLogo)) + + commGroup.GET("/conf", ui.AmWrap(Conferences)) + confGroup := commGroup.Group("/conf/:confid") + confGroup.GET("", ui.AmWrap(Topics)) + confGroup.GET("/new_topic", ui.AmWrap(NewTopicForm)) + confGroup.POST("/new_topic", ui.AmWrap(NewTopic)) return e } diff --git a/ui/amcontext.go b/ui/amcontext.go index 1d911a2..4bbbff1 100644 --- a/ui/amcontext.go +++ b/ui/amcontext.go @@ -28,6 +28,11 @@ import ( log "github.com/sirupsen/logrus" ) +/*---------------------------------------------------------------------------- + * AmContext interface + *---------------------------------------------------------------------------- + */ + // AmContext is the interface for Amsterdam's wrapper context that exposes the required functionality. type AmContext interface { ClearCommunityContext() @@ -36,7 +41,6 @@ type AmContext interface { CurrentCommunity() *database.Community CurrentUser() *database.User CurrentUserId() int32 - Done() EffectiveLevel() uint16 FormField(string) string FormFieldInt(string) (int, error) @@ -72,13 +76,17 @@ type AmContext interface { VarMap() jet.VarMap } +/*---------------------------------------------------------------------------- + * AmContext implementation + *---------------------------------------------------------------------------- + */ + // amContext is the internal structure that implements AmContext. type amContext struct { echoContext echo.Context httprc int rendervars jet.VarMap outputType string - scratchpad map[string]any session *sessions.Session globals *database.Globals globalFlags *util.OptionSet @@ -143,11 +151,6 @@ func (c *amContext) CurrentUserId() int32 { return AmSessionUid(c.session) } -// Done signals that we're done with this context and it can be recycled. -func (c *amContext) Done() { - amContextRecycleBin <- c -} - // EffectiveLevel returns the user's effective access level (in terms of current community, if any). func (c *amContext) EffectiveLevel() uint16 { return c.effectiveLevel @@ -275,14 +278,6 @@ func (c *amContext) SaveSession() error { return c.session.Save(c.echoContext.Request(), c.echoContext.Response()) } -// Scratchpad returns the per-request scratchpad for values. -func (c *amContext) Scratchpad() map[string]any { - if c.scratchpad == nil { - c.scratchpad = make(map[string]any) - } - return c.scratchpad -} - /* SubRender renders a subtemplate to the output. * Parameters: * name = The name of the template to be rendered. @@ -363,18 +358,12 @@ func (c *amContext) SetRC(rc int) { // GetScratch returns a value in the per-request scratchpad. func (c *amContext) GetScratch(name string) any { - if c.scratchpad == nil { - return nil - } - return c.scratchpad[name] + return c.echoContext.Get("am." + name) } // SetScratch sets a value in the per-request scratchpad. func (c *amContext) SetScratch(name string, val any) { - if c.scratchpad == nil { - c.scratchpad = make(map[string]any) - } - c.scratchpad[name] = val + c.echoContext.Set("am."+name, val) } // GetSession returns a session variable. @@ -431,21 +420,20 @@ var freeContext util.FreeList[amContext] // amContextRecycleBin is the channel we put contexts on to be recycled. var amContextRecycleBin chan *amContext -/* AmCreateContext creates a new AmContext wrapping the Echo context. +/* newContext creates a new AmContext wrapping the Echo context. * Parameters: * ctxt - The Echo context to be wrapped. * Returns: - * A new Amsterdam context wrapping that context. + * Internal Amsterdam context structure pointer, or nil. * Standard Go error status. */ -func AmCreateContext(ctxt echo.Context) (AmContext, error) { +func newContext(ctxt echo.Context) (*amContext, error) { rc := freeContext.Get() if rc == nil { rc = &amContext{ httprc: http.StatusOK, rendervars: make(jet.VarMap), outputType: "", - scratchpad: nil, } } @@ -460,7 +448,7 @@ func AmCreateContext(ctxt echo.Context) (AmContext, error) { } rc.echoContext = ctxt - ctxt.Set("amsterdam_context", rc) + ctxt.Set("__amsterdam_context", rc) sess, err := session.Get("AMSTERDAM_SESSION", ctxt) if err == nil { rc.session = sess @@ -491,17 +479,17 @@ func AmCreateContext(ctxt echo.Context) (AmContext, error) { * Parameters: * ctxt - The Echo context to have the AmContext extracted. * Returns: - * The associated AmContext, or nil if there is none. + * The associated AmContext. */ func AmContextFromEchoContext(ctxt echo.Context) AmContext { - myctxt := ctxt.Get("amsterdam_context") + myctxt := ctxt.Get("__amsterdam_context") if myctxt != nil { rc, ok := myctxt.(AmContext) if ok { return rc } } - return nil + panic("Failed to find AmContext when required") } // contextRecycler is the task that recycles context blocks. @@ -513,11 +501,6 @@ func contextRecycler(incoming chan *amContext, done chan bool) { delete(c.rendervars, k) } c.outputType = "" - if c.scratchpad != nil { - for k := range c.scratchpad { - delete(c.scratchpad, k) - } - } c.session = nil c.globals = nil c.globalFlags = nil @@ -541,3 +524,15 @@ func SetupAmContext() func() { <-done } } + +// ContextCreator is middleware that creates and recycles the AmContext. +func ContextCreator(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + myctxt, err := newContext(c) + if err == nil { + err = next(c) + amContextRecycleBin <- myctxt + } + return err + } +} diff --git a/ui/middleware.go b/ui/middleware.go new file mode 100644 index 0000000..cff5452 --- /dev/null +++ b/ui/middleware.go @@ -0,0 +1,89 @@ +/* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// Package ui holds the support for the Amsterdam user interface, wrapping Echo and Jet templates. +package ui + +import ( + "net/http" + "net/url" + + "git.erbosoft.com/amy/amsterdam/config" + "git.erbosoft.com/amy/amsterdam/database" + "github.com/labstack/echo/v4" + log "github.com/sirupsen/logrus" +) + +// IPBanTest is middleware that handles the IP banning. +func IPBanTest(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + // Check IP banning. + banmsg, banerr := database.AmTestIPBan(c.RealIP()) + if banerr != nil { + c.Logger().Warnf("address %s could not be tested: %v", c.RealIP(), banerr) + // but let the request pass anyway + } else if banmsg != "" { + amctxt := AmContextFromEchoContext(c) + amctxt.VarMap().Set("amsterdam_pageTitle", "IP Address Banned") + amctxt.VarMap().Set("message", banmsg) + amctxt.SetRC(http.StatusForbidden) + return AmSendPageData(c, amctxt, "framed_template", "ipban.jet") + } + return next(c) + } +} + +// CookieLoginTest is middleware that handles cookie logins. +func CookieLoginTest(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + amctxt := AmContextFromEchoContext(c) + // Check for cookie login. + if amctxt.CurrentUser().IsAnon { + cookie, err := c.Cookie(config.GlobalConfig.Site.LoginCookieName) + if err == nil { + var user *database.User + user, err = database.AmAuthenticateUserByToken(cookie.Value, c.RealIP()) + if err == nil { + // log the user in and rotate login cookie + amctxt.ReplaceUser(user) + var newToken string + if newToken, err = user.NewAuthToken(); err == nil { + amctxt.SetLoginCookie(newToken) + } else { + log.Warnf("unable to rotate login cookie: %v", err) + } + if !user.VerifyEMail { + // bounce to E-mail verification before we go anywhere + return AmSendPageData(c, amctxt, "redirect", + "/verify?tgt="+url.QueryEscape(c.Request().URL.Path)) + } + } else { + log.Errorf("login cookie bogus, do not use: %v", err) + amctxt.ClearLoginCookie() + } + } + } + return next(c) + } +} + +// SetCommunity is middleware that sets the community context based on the URL. +func SetCommunity(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ctxt := AmContextFromEchoContext(c) + err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) + if err != nil { + ctxt.SetRC(http.StatusNotFound) + cmd, data, _ := ErrorPage(ctxt, err) + return AmSendPageData(c, ctxt, cmd, data) + } + ctxt.SetLeftMenu("community") + return next(c) + } +} diff --git a/ui/render_wrap.go b/ui/render_wrap.go index 3781b4b..3e6d3da 100644 --- a/ui/render_wrap.go +++ b/ui/render_wrap.go @@ -13,14 +13,26 @@ package ui import ( "fmt" "net/http" - "net/url" - "git.erbosoft.com/amy/amsterdam/config" - "git.erbosoft.com/amy/amsterdam/database" "github.com/labstack/echo/v4" log "github.com/sirupsen/logrus" ) +/* AmSendPageData sends page data to the output based on the command string. + * Parameters: + * ctxt - The Echo context from the request. + * amctxt - The associated AmContext. + * command - The type of rendering to be done. Known values are: + * "bytes" - Output "data" as a byte array. + * "redirect" - Treat "data" as a URL to be redirected to and send a 302 Redirect. + * "string" - Output "data" as a string. + * "template" - Treat "data" as a template name, and output that template. + * "framed_template" - Treat "data" as an inner template name, and output that template rendered + * within the outer "frame.jet" template. + * data - The data to be output, as determined by the command. + * Returns: + * Standard Go error status. + */ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data any) error { var err error switch command { @@ -29,9 +41,9 @@ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data an case "redirect": err = ctxt.Redirect(http.StatusFound, data.(string)) case "string": - err = ctxt.String(amctxt.RC(), fmt.Sprintf("%v", data)) + err = ctxt.String(amctxt.RC(), data.(string)) case "template": - err = ctxt.Render(amctxt.RC(), fmt.Sprintf("%v", data), amctxt) + err = ctxt.Render(amctxt.RC(), data.(string), amctxt) case "framed_template": amctxt.VarMap().Set("amsterdam_innerPage", data) menus := make([]*MenuDefinition, 2) @@ -86,53 +98,7 @@ func ErrorPage(ctxt AmContext, input_err error) (string, any, error) { */ func AmWrap(myfunc func(AmContext) (string, any, error)) echo.HandlerFunc { return func(ctxt echo.Context) error { - // Create the AmContext. - amctxt, aerr := AmCreateContext(ctxt) - if aerr != nil { - ctxt.Logger().Errorf("Session creation error: %v", aerr) - return aerr - } - defer amctxt.Done() - - // Check IP banning. - banmsg, banerr := database.AmTestIPBan(ctxt.RealIP()) - if banerr != nil { - ctxt.Logger().Warnf("address %s could not be tested: %v", ctxt.RealIP(), banerr) - // but let the request pass anyway - } else if banmsg != "" { - amctxt.VarMap().Set("amsterdam_pageTitle", "IP Address Banned") - amctxt.VarMap().Set("message", banmsg) - amctxt.SetRC(http.StatusForbidden) - return AmSendPageData(ctxt, amctxt, "framed_template", "ipban.jet") - } - - // Check for cookie login. - if amctxt.CurrentUser().IsAnon { - cookie, cerr := ctxt.Cookie(config.GlobalConfig.Site.LoginCookieName) - if cerr == nil { - var user *database.User - user, cerr = database.AmAuthenticateUserByToken(cookie.Value, ctxt.RealIP()) - if cerr == nil { - // log the user in and rotate login cookie - amctxt.ReplaceUser(user) - var newToken string - if newToken, cerr = user.NewAuthToken(); cerr == nil { - amctxt.SetLoginCookie(newToken) - } else { - log.Warnf("unable to rotate login cookie: %v", cerr) - } - if !user.VerifyEMail { - // bounce to E-mail verification before we go anywhere - return AmSendPageData(ctxt, amctxt, "redirect", - "/verify?tgt="+url.QueryEscape(ctxt.Request().URL.Path)) - } - } else { - log.Errorf("login cookie bogus, do not use: %v", cerr) - amctxt.ClearLoginCookie() - } - } - } - + amctxt := AmContextFromEchoContext(ctxt) // Exec the wrapped function. what, rc, err := myfunc(amctxt) if err == nil {