ikurotime / gitgud

public
main / internal/interface/web/browse_handler.go
4.1 KB · Go Raw
  1package web
  2
  3import (
  4	"errors"
  5	"log"
  6	"net/http"
  7	"strings"
  8
  9	"github.com/go-chi/chi"
 10
 11	"gitgud/internal/domain"
 12	"gitgud/internal/interface/web/presenter"
 13	"gitgud/internal/interface/web/templates"
 14)
 15
 16func (h *Handlers) repoTreeRoot(w http.ResponseWriter, r *http.Request) {
 17	repo := h.viewRepo(w, r)
 18	if repo == nil {
 19		return
 20	}
 21	h.renderTree(w, r, repo, "", "")
 22}
 23
 24func (h *Handlers) repoTree(w http.ResponseWriter, r *http.Request) {
 25	repo := h.viewRepo(w, r)
 26	if repo == nil {
 27		return
 28	}
 29	h.renderTree(w, r, repo, chi.URLParam(r, "ref"), chi.URLParam(r, "*"))
 30}
 31
 32func (h *Handlers) renderTree(w http.ResponseWriter, r *http.Request, repo *domain.Repository, ref, path string) {
 33	ctx := r.Context()
 34
 35	empty, err := h.browse.IsEmpty(ctx, repo)
 36	if err != nil {
 37		h.writeError(w, r, err)
 38		return
 39	}
 40	if empty {
 41		render(w, r, http.StatusOK, templates.RepoHome(currentUser(ctx), repo))
 42		return
 43	}
 44
 45	if ref == "" {
 46		ref = repo.DefaultBranch
 47	}
 48
 49	branches, err := h.browse.Branches(ctx, repo)
 50	if err != nil {
 51		h.writeError(w, r, err)
 52		return
 53	}
 54	entries, err := h.browse.Tree(ctx, repo, ref, path)
 55	if err != nil {
 56		h.writeError(w, r, err)
 57		return
 58	}
 59
 60	readme := ""
 61	for _, e := range entries {
 62		if !e.IsDir && strings.EqualFold(e.Name, "README.md") {
 63			if blob, err := h.browse.Blob(ctx, repo, ref, e.Path); err == nil && !blob.IsBinary {
 64				readme = presenter.RenderMarkdown(blob.Content)
 65			}
 66			break
 67		}
 68	}
 69
 70	render(w, r, http.StatusOK, templates.BrowseTree(currentUser(ctx), repo, ref, path, branches, entries, readme))
 71}
 72
 73func (h *Handlers) repoBlob(w http.ResponseWriter, r *http.Request) {
 74	repo := h.viewRepo(w, r)
 75	if repo == nil {
 76		return
 77	}
 78	ref := chi.URLParam(r, "ref")
 79	path := chi.URLParam(r, "*")
 80
 81	blob, err := h.browse.Blob(r.Context(), repo, ref, path)
 82	if err != nil {
 83		h.writeError(w, r, err)
 84		return
 85	}
 86	if ref == "" {
 87		ref = repo.DefaultBranch
 88	}
 89
 90	highlighted := ""
 91	if !blob.IsBinary {
 92		highlighted = presenter.Highlight(string(blob.Content), blob.Path)
 93	}
 94	render(w, r, http.StatusOK, templates.BrowseBlob(currentUser(r.Context()), repo, ref, blob, highlighted))
 95}
 96
 97func (h *Handlers) repoRaw(w http.ResponseWriter, r *http.Request) {
 98	repo := h.viewRepo(w, r)
 99	if repo == nil {
100		return
101	}
102	blob, err := h.browse.Blob(r.Context(), repo, chi.URLParam(r, "ref"), chi.URLParam(r, "*"))
103	if err != nil {
104		h.writeError(w, r, err)
105		return
106	}
107
108	ctype := http.DetectContentType(blob.Content)
109	if !blob.IsBinary {
110		ctype = "text/plain; charset=utf-8"
111	}
112	w.Header().Set("Content-Type", ctype)
113	w.Write(blob.Content)
114}
115
116func (h *Handlers) repoCommits(w http.ResponseWriter, r *http.Request) {
117	repo := h.viewRepo(w, r)
118	if repo == nil {
119		return
120	}
121	ref := chi.URLParam(r, "ref")
122
123	commits, err := h.browse.Log(r.Context(), repo, ref, 50, 0)
124	if err != nil {
125		h.writeError(w, r, err)
126		return
127	}
128	if ref == "" {
129		ref = repo.DefaultBranch
130	}
131	render(w, r, http.StatusOK, templates.Commits(currentUser(r.Context()), repo, ref, commits))
132}
133
134func (h *Handlers) repoCommit(w http.ResponseWriter, r *http.Request) {
135	repo := h.viewRepo(w, r)
136	if repo == nil {
137		return
138	}
139	commit, diffs, err := h.browse.CommitDiff(r.Context(), repo, chi.URLParam(r, "hash"))
140	if err != nil {
141		h.writeError(w, r, err)
142		return
143	}
144	render(w, r, http.StatusOK, templates.CommitDetail(currentUser(r.Context()), repo, commit, diffs))
145}
146
147func (h *Handlers) viewRepo(w http.ResponseWriter, r *http.Request) *domain.Repository {
148	repo, err := h.browse.Repo(r.Context(), chi.URLParam(r, "owner"), chi.URLParam(r, "repo"), currentUser(r.Context()))
149	if err != nil {
150		h.notFound(w, r)
151		return nil
152	}
153	return repo
154}
155
156func (h *Handlers) writeError(w http.ResponseWriter, r *http.Request, err error) {
157	user := currentUser(r.Context())
158	switch {
159	case errors.Is(err, domain.ErrNotFound):
160		render(w, r, http.StatusNotFound, templates.NotFound(user))
161	case errors.Is(err, domain.ErrPermission):
162		render(w, r, http.StatusForbidden, templates.Forbidden(user))
163	case errors.Is(err, domain.ErrUnauthorized):
164		http.Redirect(w, r, "/login", http.StatusSeeOther)
165	default:
166		log.Printf("server error: %v", err)
167		render(w, r, http.StatusInternalServerError, templates.ServerError(user))
168	}
169}