// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package routes import ( "fmt" "net/http" "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "gitea.com/go-chi/session" "github.com/unrolled/render" ) type dataStore struct { Data map[string]interface{} } func (d *dataStore) GetData() map[string]interface{} { return d.Data } // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. // Although similar to macaron.Recovery() the main difference is that this error will be created // with the gitea 500 page. func Recovery() func(next http.Handler) http.Handler { var isDevelopment = setting.RunMode != "prod" return func(next http.Handler) http.Handler { rnd := render.New(render.Options{ Extensions: []string{".tmpl"}, Directory: "templates", Funcs: templates.NewFuncMap(), Asset: templates.GetAsset, AssetNames: templates.GetAssetNames, IsDevelopment: isDevelopment, }) return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { defer func() { // Why we need this? The first recover will try to render a beautiful // error page for user, but the process can still panic again, then // we have to just recover twice and send a simple error page that // should not panic any more. defer func() { if err := recover(); err != nil { combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) log.Error(combinedErr) if isDevelopment { http.Error(w, combinedErr, 500) } else { http.Error(w, http.StatusText(500), 500) } } }() if err := recover(); err != nil { combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) log.Error("%v", combinedErr) lc := middlewares.Locale(w, req) sess := session.GetSession(req) var store = dataStore{ Data: templates.Vars{ "Language": lc.Language(), "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), "i18n": lc, }, } // Get user from session if logged in. user, _ := sso.SignedInUser(req, w, &store, sess) if user != nil { store.Data["IsSigned"] = true store.Data["SignedUser"] = user store.Data["SignedUserID"] = user.ID store.Data["SignedUserName"] = user.Name store.Data["IsAdmin"] = user.IsAdmin } else { store.Data["SignedUserID"] = int64(0) store.Data["SignedUserName"] = "" } w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) if setting.RunMode != "prod" { store.Data["ErrMsg"] = combinedErr } err := rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) if err != nil { log.Error("%v", err) } } }() next.ServeHTTP(w, req) }) } }