mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
76 lines
2.6 KiB
Go
76 lines
2.6 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/modules/httpcache"
|
|
"github.com/hanzoai/git/modules/log"
|
|
"github.com/hanzoai/git/modules/reqctx"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/templates"
|
|
"github.com/hanzoai/git/modules/web/middleware"
|
|
"github.com/hanzoai/git/modules/web/routing"
|
|
"github.com/hanzoai/git/services/context"
|
|
)
|
|
|
|
const tplStatus500 templates.TplName = "status/500"
|
|
|
|
func renderServerErrorPage(w http.ResponseWriter, req *http.Request, respCode int, tmpl templates.TplName, ctxData map[string]any, plainMsg string) {
|
|
acceptsHTML := false
|
|
for _, part := range req.Header["Accept"] {
|
|
if strings.Contains(part, "text/html") {
|
|
acceptsHTML = true
|
|
break
|
|
}
|
|
}
|
|
|
|
httpcache.SetCacheControlInHeader(w.Header(), &httpcache.CacheControlOptions{})
|
|
tmplCtx := context.NewTemplateContextForWeb(reqctx.FromContext(req.Context()), req, middleware.Locale(w, req))
|
|
w.WriteHeader(respCode)
|
|
|
|
outBuf := &bytes.Buffer{}
|
|
if acceptsHTML {
|
|
err := templates.PageRenderer().HTML(outBuf, respCode, tmpl, ctxData, tmplCtx)
|
|
if err != nil {
|
|
_, _ = w.Write([]byte("Internal server error but failed to render error page template, please collect error logs and report to Hanzo Git issue tracker"))
|
|
return
|
|
}
|
|
} else {
|
|
outBuf.WriteString(plainMsg)
|
|
}
|
|
_, _ = io.Copy(w, outBuf)
|
|
}
|
|
|
|
// renderPanicErrorPage renders a 500 page with the recovered panic value, it handles the stack trace, and it never panics
|
|
func renderPanicErrorPage(w http.ResponseWriter, req *http.Request, recovered any) {
|
|
combinedErr := fmt.Errorf("%v\n%s", recovered, log.Stack(2))
|
|
log.Error("PANIC: %v", combinedErr)
|
|
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
log.Error("Panic occurs again when rendering error page: %v. Stack:\n%s", combinedErr, log.Stack(2))
|
|
}
|
|
}()
|
|
|
|
routing.UpdatePanicError(req.Context(), combinedErr)
|
|
|
|
plainMsg := "Internal Server Error"
|
|
ctxData := middleware.GetContextData(req.Context())
|
|
// This recovery handler could be called without Hanzo Git's web context, so we shouldn't touch that context too much.
|
|
// Otherwise, the 500-page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic.
|
|
user, _ := ctxData[middleware.ContextDataKeySignedUser].(*user_model.User)
|
|
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
|
plainMsg = "PANIC: " + combinedErr.Error()
|
|
ctxData["ErrorMsg"] = plainMsg
|
|
}
|
|
renderServerErrorPage(w, req, http.StatusInternalServerError, tplStatus500, ctxData, plainMsg)
|
|
}
|