straightened out error handling so we don't wind up with a bunch of panics clogging the debug console

This commit is contained in:
2026-02-22 23:04:49 -07:00
parent 7b40c04897
commit 4db82f63d5
4 changed files with 90 additions and 30 deletions
+17 -3
View File
@@ -11,6 +11,7 @@ package main
import (
"errors"
"fmt"
"net/http"
"git.erbosoft.com/amy/amsterdam/ui"
@@ -42,7 +43,6 @@ var EPARAM error = errors.New("no parameters specified")
* Returns:
* Command string dictating what to be rendered.
* Data as a parameter for the command string.
* Standard Go error status.
*/
func NotImplPage(ctxt ui.AmContext) (string, any) {
ctxt.SetLeftMenu("top")
@@ -51,17 +51,31 @@ func NotImplPage(ctxt ui.AmContext) (string, any) {
return "framed", "notimpl.jet"
}
/* AmNotFoundHandler handles all paths that are "not found" in the application.
* Parameters:
* ctxt - The AmContext for the request.
* Returns:
* Command string dictating what to be rendered.
* Data as a parameter for the command string.
*/
func AmNotFoundHandler(ctxt ui.AmContext) (string, any) {
log.Infof("-> AmNotFoundHandler on path %s", ctxt.URLPath())
return "error", fmt.Errorf("Path not found: %s", ctxt.URLPath())
}
/* AmErrorHandler handles all the mundane HTTP errors generated by the Echo engine.
* Parameters:
* err - The error to be handled.
* c - The Echo context error is being handled on.
*/
func AmErrorHandler(err error, c echo.Context) {
log.Infof("-> AmErrorHandler on path %s", c.Request().URL.Path)
if c.Response().Committed {
return
}
amctxt := ui.AmContextFromEchoContext(c)
cerr := ui.AmSendPageData(c, amctxt, "error", err)
cerr := ui.AmWithTempContext(c, func(ctxt ui.AmContext) (string, any) {
return "error", err
})
if cerr != nil {
log.Errorf("Error rendering error (%v): %v", err, cerr)
}
+1
View File
@@ -51,6 +51,7 @@ func setupEcho() *echo.Echo {
// This is the set of all middleware functions used by the UI, as opposed to other things.
uiset := []echo.MiddlewareFunc{ui.SessionStoreInjector, ui.ContextCreator, ui.IPBanTest, ui.CookieLoginTest}
e.RouteNotFound("/*", ui.AmWrap(AmNotFoundHandler), uiset...)
e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...)
e.GET("/img/*", ui.AmServeImage)
e.GET("/static/*", ui.AmStaticFileHandler())
+26 -23
View File
@@ -549,34 +549,37 @@ func newContext(ctxt echo.Context) (*amContext, error) {
rc.echoContext = ctxt
ctxt.Set("__amsterdam_context", rc)
store := ctxt.Get("AmSessionStore").(AmSessionStore)
sess, err := store.Get(ctxt.Request(), "AMSTERDAM_SESSION")
if err == nil {
rc.session = sess
sess.SetOptions(defoptions)
if sess.IsNew() {
sess.FirstTime(ctxt.Request().Context())
} else {
sess.Hit()
}
}
id, ok := sess.Uid()
if ok {
rc.user, err = database.AmGetUser(ctxt.Request().Context(), id)
stmp := ctxt.Get("AmSessionStore")
if stmp != nil {
store := stmp.(AmSessionStore)
sess, err := store.Get(ctxt.Request(), "AMSTERDAM_SESSION")
if err == nil {
rc.effectiveLevel = rc.user.BaseLevel
rc.session = sess
sess.SetOptions(defoptions)
if sess.IsNew() {
sess.FirstTime(ctxt.Request().Context())
} else {
sess.Hit()
}
}
id, ok := sess.Uid()
if ok {
rc.user, err = database.AmGetUser(ctxt.Request().Context(), id)
if err == nil {
rc.effectiveLevel = rc.user.BaseLevel
} else {
rc.user = nil
rc.effectiveLevel = database.AmRole("NotInList").Level()
}
} else {
rc.user = nil
rc.effectiveLevel = database.AmRole("NotInList").Level()
}
} else {
rc.user = nil
rc.effectiveLevel = database.AmRole("NotInList").Level()
}
if rc.user != nil && !rc.user.IsAnon {
cp, ok := sess.Get("lastCommunity")
if ok {
rc.SetCommunityContext(fmt.Sprintf("%d", cp))
if rc.user != nil && !rc.user.IsAnon {
cp, ok := sess.Get("lastCommunity")
if ok {
rc.SetCommunityContext(fmt.Sprintf("%d", cp))
}
}
}
return rc, err
+46 -4
View File
@@ -114,11 +114,16 @@ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data an
case "top":
menus[0] = AmMenu("top")
case "community":
md, err := AmBuildCommunityMenu(ctxt.Request().Context(), amctxt.CurrentCommunity())
if err != nil {
return err
comm := amctxt.CurrentCommunity()
if comm != nil {
md, err := AmBuildCommunityMenu(ctxt.Request().Context(), comm)
if err != nil {
return err
}
menus[0] = md
} else {
menus[0] = AmMenu("top")
}
menus[0] = md
default:
return fmt.Errorf("AmSendPageData(): unknown left menu context: %s", amctxt.LeftMenu())
}
@@ -175,3 +180,40 @@ func AmWrap(myfunc AmPageFunc) echo.HandlerFunc {
return nil
}
}
// AmWithTempContext runs a page function with a temporary context. Used in error handling.
func AmWithTempContext(c echo.Context, fn AmPageFunc) error {
var ctxt AmContext = nil
myctxt := c.Get("__amsterdam_context")
if myctxt != nil {
ac, ok := myctxt.(*amContext)
if ok {
ctxt = ac
ac.echoContext = c
}
}
if ctxt == nil {
ac, err := newContext(c)
if err != nil {
return err
}
ctxt = ac
defer func() {
amContextRecycleBin <- ac
}()
}
// Call the function
command, arg := fn(ctxt)
// Add the dynamic headers.
c.Response().Header().Set("Pragma", "No-cache")
c.Response().Header().Set("Cache-Control", "no-cache")
c.Response().Header().Set("Expires", expireTime)
if err := AmSendPageData(c, ctxt, command, arg); err != nil {
c.Logger().Errorf("Rendering error: %v", err)
return err
}
return nil
}