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