| @@ -55,6 +55,19 @@ func (s *Server) favicon(w http.ResponseWriter, r *http.Request) { |
| 55 | 55 | w.Write(web.FaviconSVG) |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | // notFound renders the designed 404 page with a 404 status. Falls back to |
| 59 | // the stock plain-text response if the template fails. |
| 60 | func (s *Server) notFound(w http.ResponseWriter, r *http.Request) { |
| 61 | var buf bytes.Buffer |
| 62 | if err := web.Render(&buf, "404.html", struct{ Site string }{s.siteName()}); err != nil { |
| 63 | http.NotFound(w, r) |
| 64 | return |
| 65 | } |
| 66 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 67 | w.WriteHeader(http.StatusNotFound) |
| 68 | buf.WriteTo(w) |
| 69 | } |
| 70 | |
| 58 | 71 | // describedRepo pairs a repo with its description for listings. |
| 59 | 72 | type describedRepo struct { |
| 60 | 73 | store.Repo |
| @@ -129,7 +142,7 @@ func (s *Server) repoFor(w http.ResponseWriter, r *http.Request, ref string) (re |
| 129 | 142 | ok = policyCanRead(viewer, repo, grant) |
| 130 | 143 | } |
| 131 | 144 | if !ok { |
| 132 | | http.NotFound(w, r) |
| 145 | s.notFound(w, r) |
| 133 | 146 | return repoPage{}, false |
| 134 | 147 | } |
| 135 | 148 | if ref == "" { |
| @@ -186,7 +199,7 @@ func (s *Server) ownerPage(w http.ResponseWriter, r *http.Request) { |
| 186 | 199 | kind, ownerID = "org", o.ID |
| 187 | 200 | members, _ = s.st.OrgMembers(o.ID) |
| 188 | 201 | } else { |
| 189 | | http.NotFound(w, r) |
| 202 | s.notFound(w, r) |
| 190 | 203 | return |
| 191 | 204 | } |
| 192 | 205 | profile, _ := s.st.OwnerProfile(kind, ownerID) |
| @@ -251,7 +264,7 @@ func (s *Server) renderTree(w http.ResponseWriter, r *http.Request, p repoPage, |
| 251 | 264 | } |
| 252 | 265 | entries, err := gitutil.ListTree(p.Dir, p.Ref, dirPath) |
| 253 | 266 | if err != nil { |
| 254 | | http.NotFound(w, r) |
| 267 | s.notFound(w, r) |
| 255 | 268 | return |
| 256 | 269 | } |
| 257 | 270 | prefix := "" |
| @@ -286,7 +299,7 @@ func (s *Server) blob(w http.ResponseWriter, r *http.Request) { |
| 286 | 299 | filePath := strings.Trim(r.PathValue("path"), "/") |
| 287 | 300 | data, err := gitutil.ReadBlob(p.Dir, p.Ref, filePath, maxRenderBytes+1) |
| 288 | 301 | if err != nil { |
| 289 | | http.NotFound(w, r) |
| 302 | s.notFound(w, r) |
| 290 | 303 | return |
| 291 | 304 | } |
| 292 | 305 | binary := gitutil.IsBinary(data) || len(data) > maxRenderBytes |
| @@ -338,7 +351,7 @@ func (s *Server) raw(w http.ResponseWriter, r *http.Request) { |
| 338 | 351 | filePath := strings.Trim(r.PathValue("path"), "/") |
| 339 | 352 | data, err := gitutil.ReadBlob(p.Dir, p.Ref, filePath, s.cfg.Limits.MaxBlobBytes) |
| 340 | 353 | if err != nil { |
| 341 | | http.NotFound(w, r) |
| 354 | s.notFound(w, r) |
| 342 | 355 | return |
| 343 | 356 | } |
| 344 | 357 | // Serve inert: never let repo content execute in the forge's origin. |
| @@ -577,7 +590,7 @@ func (s *Server) log(w http.ResponseWriter, r *http.Request) { |
| 577 | 590 | const pageSize = 50 |
| 578 | 591 | shas, err := gitutil.RevList(p.Dir, p.Ref, pageSize+1) |
| 579 | 592 | if err != nil { |
| 580 | | http.NotFound(w, r) |
| 593 | s.notFound(w, r) |
| 581 | 594 | return |
| 582 | 595 | } |
| 583 | 596 | next := "" |
| @@ -617,12 +630,12 @@ func (s *Server) commit(w http.ResponseWriter, r *http.Request) { |
| 617 | 630 | sha := r.PathValue("sha") |
| 618 | 631 | full, err := gitutil.ResolveRef(p.Dir, sha) |
| 619 | 632 | if err != nil { |
| 620 | | http.NotFound(w, r) |
| 633 | s.notFound(w, r) |
| 621 | 634 | return |
| 622 | 635 | } |
| 623 | 636 | v, parsed := s.sigFor(p.Repo, p.Dir, full) |
| 624 | 637 | if parsed == nil { |
| 625 | | http.NotFound(w, r) |
| 638 | s.notFound(w, r) |
| 626 | 639 | return |
| 627 | 640 | } |
| 628 | 641 | patch, _ := gitutil.ShowPatch(p.Dir, full, 4<<20) |
| @@ -708,12 +721,12 @@ func (s *Server) issue(w http.ResponseWriter, r *http.Request) { |
| 708 | 721 | p.Tab = "issues" |
| 709 | 722 | n, err := strconv.ParseInt(r.PathValue("n"), 10, 64) |
| 710 | 723 | if err != nil { |
| 711 | | http.NotFound(w, r) |
| 724 | s.notFound(w, r) |
| 712 | 725 | return |
| 713 | 726 | } |
| 714 | 727 | iss, err := s.st.IssueByNumber(p.Repo.ID, n) |
| 715 | 728 | if err != nil { |
| 716 | | http.NotFound(w, r) |
| 729 | s.notFound(w, r) |
| 717 | 730 | return |
| 718 | 731 | } |
| 719 | 732 | comments, err := s.st.ListIssueComments(iss.ID) |
| @@ -764,12 +777,12 @@ func (s *Server) mr(w http.ResponseWriter, r *http.Request) { |
| 764 | 777 | p.Tab = "merge requests" |
| 765 | 778 | n, err := strconv.ParseInt(r.PathValue("n"), 10, 64) |
| 766 | 779 | if err != nil { |
| 767 | | http.NotFound(w, r) |
| 780 | s.notFound(w, r) |
| 768 | 781 | return |
| 769 | 782 | } |
| 770 | 783 | m, err := s.st.MRByNumber(p.Repo.ID, n) |
| 771 | 784 | if err != nil { |
| 772 | | http.NotFound(w, r) |
| 785 | s.notFound(w, r) |
| 773 | 786 | return |
| 774 | 787 | } |
| 775 | 788 | comments, _ := s.st.ListMRComments(m.ID) |
| @@ -827,11 +840,11 @@ func (s *Server) archive(w http.ResponseWriter, r *http.Request) { |
| 827 | 840 | file := r.PathValue("file") |
| 828 | 841 | ref, ok := strings.CutSuffix(file, ".tar.gz") |
| 829 | 842 | if !ok { |
| 830 | | http.NotFound(w, r) |
| 843 | s.notFound(w, r) |
| 831 | 844 | return |
| 832 | 845 | } |
| 833 | 846 | if _, err := gitutil.ResolveRef(p.Dir, ref); err != nil { |
| 834 | | http.NotFound(w, r) |
| 847 | s.notFound(w, r) |
| 835 | 848 | return |
| 836 | 849 | } |
| 837 | 850 | prefix := fmt.Sprintf("%s-%s", p.Repo.Name, ref) |