code.gitea.io/gitea@v1.22.3/services/context/context_response.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package context 5 6 import ( 7 "errors" 8 "fmt" 9 "html/template" 10 "net" 11 "net/http" 12 "net/url" 13 "path" 14 "strconv" 15 "strings" 16 "syscall" 17 "time" 18 19 user_model "code.gitea.io/gitea/models/user" 20 "code.gitea.io/gitea/modules/base" 21 "code.gitea.io/gitea/modules/httplib" 22 "code.gitea.io/gitea/modules/log" 23 "code.gitea.io/gitea/modules/setting" 24 "code.gitea.io/gitea/modules/templates" 25 "code.gitea.io/gitea/modules/web/middleware" 26 ) 27 28 // RedirectToUser redirect to a differently-named user 29 func RedirectToUser(ctx *Base, userName string, redirectUserID int64) { 30 user, err := user_model.GetUserByID(ctx, redirectUserID) 31 if err != nil { 32 ctx.Error(http.StatusInternalServerError, "unable to get user") 33 return 34 } 35 36 redirectPath := strings.Replace( 37 ctx.Req.URL.EscapedPath(), 38 url.PathEscape(userName), 39 url.PathEscape(user.Name), 40 1, 41 ) 42 if ctx.Req.URL.RawQuery != "" { 43 redirectPath += "?" + ctx.Req.URL.RawQuery 44 } 45 ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect) 46 } 47 48 // RedirectToCurrentSite redirects to first not empty URL which belongs to current site 49 func (ctx *Context) RedirectToCurrentSite(location ...string) { 50 for _, loc := range location { 51 if len(loc) == 0 { 52 continue 53 } 54 55 if !httplib.IsCurrentGiteaSiteURL(ctx, loc) { 56 continue 57 } 58 59 ctx.Redirect(loc) 60 return 61 } 62 63 ctx.Redirect(setting.AppSubURL + "/") 64 } 65 66 const tplStatus500 base.TplName = "status/500" 67 68 // HTML calls Context.HTML and renders the template to HTTP response 69 func (ctx *Context) HTML(status int, name base.TplName) { 70 log.Debug("Template: %s", name) 71 72 tmplStartTime := time.Now() 73 if !setting.IsProd { 74 ctx.Data["TemplateName"] = name 75 } 76 ctx.Data["TemplateLoadTimes"] = func() string { 77 return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms" 78 } 79 80 err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext) 81 if err == nil || errors.Is(err, syscall.EPIPE) { 82 return 83 } 84 85 // if rendering fails, show error page 86 if name != tplStatus500 { 87 err = fmt.Errorf("failed to render template: %s, error: %s", name, templates.HandleTemplateRenderingError(err)) 88 ctx.ServerError("Render failed", err) // show the 500 error page 89 } else { 90 ctx.PlainText(http.StatusInternalServerError, "Unable to render status/500 page, the template system is broken, or Gitea can't find your template files.") 91 return 92 } 93 } 94 95 // JSONTemplate renders the template as JSON response 96 // keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape 97 func (ctx *Context) JSONTemplate(tmpl base.TplName) { 98 t, err := ctx.Render.TemplateLookup(string(tmpl), nil) 99 if err != nil { 100 ctx.ServerError("unable to find template", err) 101 return 102 } 103 ctx.Resp.Header().Set("Content-Type", "application/json") 104 if err = t.Execute(ctx.Resp, ctx.Data); err != nil { 105 ctx.ServerError("unable to execute template", err) 106 } 107 } 108 109 // RenderToHTML renders the template content to a HTML string 110 func (ctx *Context) RenderToHTML(name base.TplName, data map[string]any) (template.HTML, error) { 111 var buf strings.Builder 112 err := ctx.Render.HTML(&buf, 0, string(name), data, ctx.TemplateContext) 113 return template.HTML(buf.String()), err 114 } 115 116 // RenderWithErr used for page has form validation but need to prompt error to users. 117 func (ctx *Context) RenderWithErr(msg any, tpl base.TplName, form any) { 118 if form != nil { 119 middleware.AssignForm(form, ctx.Data) 120 } 121 ctx.Flash.Error(msg, true) 122 ctx.HTML(http.StatusOK, tpl) 123 } 124 125 // NotFound displays a 404 (Not Found) page and prints the given error, if any. 126 func (ctx *Context) NotFound(logMsg string, logErr error) { 127 ctx.notFoundInternal(logMsg, logErr) 128 } 129 130 func (ctx *Context) notFoundInternal(logMsg string, logErr error) { 131 if logErr != nil { 132 log.Log(2, log.DEBUG, "%s: %v", logMsg, logErr) 133 if !setting.IsProd { 134 ctx.Data["ErrorMsg"] = logErr 135 } 136 } 137 138 // response simple message if Accept isn't text/html 139 showHTML := false 140 for _, part := range ctx.Req.Header["Accept"] { 141 if strings.Contains(part, "text/html") { 142 showHTML = true 143 break 144 } 145 } 146 147 if !showHTML { 148 ctx.plainTextInternal(3, http.StatusNotFound, []byte("Not found.\n")) 149 return 150 } 151 152 ctx.Data["IsRepo"] = ctx.Repo.Repository != nil 153 ctx.Data["Title"] = "Page Not Found" 154 ctx.HTML(http.StatusNotFound, base.TplName("status/404")) 155 } 156 157 // ServerError displays a 500 (Internal Server Error) page and prints the given error, if any. 158 func (ctx *Context) ServerError(logMsg string, logErr error) { 159 ctx.serverErrorInternal(logMsg, logErr) 160 } 161 162 func (ctx *Context) serverErrorInternal(logMsg string, logErr error) { 163 if logErr != nil { 164 log.ErrorWithSkip(2, "%s: %v", logMsg, logErr) 165 if _, ok := logErr.(*net.OpError); ok || errors.Is(logErr, &net.OpError{}) { 166 // This is an error within the underlying connection 167 // and further rendering will not work so just return 168 return 169 } 170 171 // it's safe to show internal error to admin users, and it helps 172 if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) { 173 ctx.Data["ErrorMsg"] = fmt.Sprintf("%s, %s", logMsg, logErr) 174 } 175 } 176 177 ctx.Data["Title"] = "Internal Server Error" 178 ctx.HTML(http.StatusInternalServerError, tplStatus500) 179 } 180 181 // NotFoundOrServerError use error check function to determine if the error 182 // is about not found. It responds with 404 status code for not found error, 183 // or error context description for logging purpose of 500 server error. 184 // TODO: remove the "errCheck" and use util.ErrNotFound to check 185 func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) { 186 if errCheck(logErr) { 187 ctx.notFoundInternal(logMsg, logErr) 188 return 189 } 190 ctx.serverErrorInternal(logMsg, logErr) 191 }