diff --git a/config/config.go b/config/config.go index 58d3fd5..e1ffaf7 100644 --- a/config/config.go +++ b/config/config.go @@ -29,6 +29,7 @@ const AMSTERDAM_COPYRIGHT = "2025" // AmCLI is the command-line interface arguments structure. type AmCLI struct { ConfigFile string `arg:"-C,--config" help:"Location of the configuration file."` + DebugPanic bool `arg:"--debug-panic" help:"Development Only - disable Echo panic recovery"` } // CommandLine is the command-line arguments passed to Amsterdam. diff --git a/main.go b/main.go index 67a5072..b674753 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ import ( "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + log "github.com/sirupsen/logrus" ) // setupEcho creates, configures, and returns a new Echo instance. @@ -32,7 +33,11 @@ func setupEcho() *echo.Echo { e := echo.New() e.Logger = &EchoLogrusAdapter{} e.Renderer = &ui.TemplateRenderer{} - e.Use(middleware.Recover()) + if !config.CommandLine.DebugPanic { + e.Use(middleware.Recover()) + } else { + log.Warn("WARNING: --debug-panic in effect - DO NOT use this in production!") + } e.Use(LogrusMiddleware) e.Use(session.Middleware(ui.SessionStore)) diff --git a/setup/database.sql b/setup/database.sql index b3f2917..cf617c6 100644 --- a/setup/database.sql +++ b/setup/database.sql @@ -986,7 +986,7 @@ INSERT INTO communities (commid, createdate, read_lvl, write_lvl, create_lvl, de host_uid, catid, membersonly, init_ftr, commname, language, synopsis, rules, alias) VALUES (2, '2000-12-01 00:00:00', 100, 58000, 58000, 65500, 500, 4, 2, 0, 1, 0, 'Coffeehouse', 'en-US', 'A gathering place for news and information for all Amsterdam users.', - 'Like the man said, do unto others as you would have them do unto you.', 'Piazza'); + 'Like the man said, do unto others as you would have them do unto you.', 'Coffeehouse'); INSERT INTO contacts (contactid, locality, country, owner_uid, owner_commid) VALUES (4, 'Anywhere', 'US', 2, 2); INSERT INTO propcomm (cid, ndx, data) VALUES (2, 0, ''); @@ -1001,7 +1001,7 @@ INSERT INTO commftrs (commid, ftr_code) INSERT INTO commmember (commid, uid, granted_lvl, locked, hidden) VALUES (2, 1, 100, 1, 1); -# Make the 'Administrator' user the host of La Piazza. +# Make the 'Administrator' user the host of Coffeehouse. INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (2, 2, 58500, 1); diff --git a/ui/amcontext.go b/ui/amcontext.go index 0df599d..1cd5cf8 100644 --- a/ui/amcontext.go +++ b/ui/amcontext.go @@ -43,6 +43,7 @@ type AmContext interface { Globals() *database.Globals GlobalFlags() *util.OptionSet IsMember() bool + IsMemberLocked() bool LeftMenu() string RC() int OutputType() string @@ -79,6 +80,7 @@ type amContext struct { effectiveLevel uint16 community *database.Community isMember bool + isMemberLocked bool } // ClearLoginCookie overwrites and removes the login cookie. @@ -186,6 +188,11 @@ func (c *amContext) IsMember() bool { return c.isMember } +// IsMemberLocked returns true if the user is a "locked" member of the currentr community (cannot unjoin). +func (c *amContext) IsMemberLocked() bool { + return c.isMemberLocked +} + // LeftMenu returns the current left menu selector. func (c *amContext) LeftMenu() string { return c.session.Values["leftMenu"].(string) @@ -272,12 +279,13 @@ func (c *amContext) SetCommunityContext(param string) error { if err != nil { return err } - mbr, _, level, err := comm.Membership(c.CurrentUser()) + mbr, lock, level, err := comm.Membership(c.CurrentUser()) if err != nil { return err } c.community = comm c.isMember = mbr + c.isMemberLocked = lock if level > c.effectiveLevel { c.effectiveLevel = level } @@ -454,6 +462,7 @@ func contextRecycler(incoming chan *amContext, done chan bool) { c.effectiveLevel = 0 c.community = nil c.isMember = false + c.isMemberLocked = false freeContext.Put(c) } done <- true diff --git a/ui/images.go b/ui/images.go index 8c8e34b..9281bcb 100644 --- a/ui/images.go +++ b/ui/images.go @@ -14,6 +14,7 @@ import ( "bytes" "embed" "errors" + "fmt" "image" "image/gif" "image/jpeg" @@ -85,6 +86,9 @@ func AmServeImage(ctxt AmContext) (string, any, error) { } } ctxt.SetRC(http.StatusNotFound) + if err == nil { + err = fmt.Errorf("image not found: %s", ctxt.URLPath()) + } return ErrorPage(ctxt, err) } diff --git a/ui/menus.go b/ui/menus.go index 540ec41..8edfe0a 100644 --- a/ui/menus.go +++ b/ui/menus.go @@ -135,10 +135,18 @@ func AmBuildCommunityMenu(comm *database.Community) (*MenuDefinition, error) { return a.LinkSequence - b.LinkSequence }) mia := make([]MenuItem, len(sdef)) + md := MenuDefinition{ + ID: "community", + Title: comm.Name, + PermSet: "community", + Tag: "community", + Items: mia, + } for i, sd := range sdef { mia[i].Text = sd.Title mia[i].Link = strings.ReplaceAll(sd.Link, "[CID]", comm.Alias) mia[i].Disabled = false + mia[i].P = &md if sd.RequirePermission == "" { if sd.RequireRole == "" { mia[i].Permission = "" @@ -156,13 +164,6 @@ func AmBuildCommunityMenu(comm *database.Community) (*MenuDefinition, error) { mia[i].Permission = fmt.Sprintf("%d", v1) } } - md := MenuDefinition{ - ID: "community", - Title: comm.Name, - PermSet: "community", - Items: mia, - Tag: "community", - } menuCache.Add(comm.Id, &md) return &md, nil } diff --git a/ui/render_wrap.go b/ui/render_wrap.go index 06ee36f..b72bf8d 100644 --- a/ui/render_wrap.go +++ b/ui/render_wrap.go @@ -69,6 +69,9 @@ func sendPageData(ctxt echo.Context, amctxt AmContext, command string, data any) * Standard Go error status. */ func ErrorPage(ctxt AmContext, input_err error) (string, any, error) { + if input_err == nil { + log.Error("ErrorPage called with nil input error, WTF?") + } ctxt.VarMap().Set("amsterdam_pageTitle", "Internal Server Error") ctxt.VarMap().Set("error", input_err.Error()) return "framed_template", "error.jet", nil diff --git a/ui/views/comprofile.jet b/ui/views/comprofile.jet index cde403e..e3f0af2 100644 --- a/ui/views/comprofile.jet +++ b/ui/views/comprofile.jet @@ -19,7 +19,7 @@