~bigbes/sourcehut-dolt

ref: 2dfab0430aafd5c239d92df9feba482e0e770b6a sourcehut-dolt/web/handlers_browse.go -rw-r--r-- 5.1 KiB
2dfab043 — Eugene Blikh rename module to sourcecraft.dev/bigbes/sr-ht-dolt; depend on sourcecraft sr-ht-core 30 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package web

import (
	"errors"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"

	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
	"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)

const (
	// logPageSize is the number of commits per /log page.
	logPageSize = 20
	// rowsPageSize is the number of rows per /table page.
	rowsPageSize = 50
)

// openBrowse opens a browse session for repo, writing a 500 and returning
// ok=false on failure. The caller must Close the returned session.
func (a *app) openBrowse(w http.ResponseWriter, r *http.Request, repo *core.Repo) (BrowseSession, bool) {
	sess, err := a.cfg.Browse.Open(r.Context(), repo.Path)
	if err != nil {
		http.Error(w, "failed to open database", http.StatusInternalServerError)
		return nil, false
	}
	return sess, true
}

// handleLog renders a paginated commit log for a branch (or ref). Pages after
// the first are reached via ?from=<hash> (the nextHash from the prior page); the
// active branch is chosen with ?branch=.
func (a *app) handleLog(w http.ResponseWriter, r *http.Request) {
	repo, _, _, ok := a.loadRepoForBrowse(w, r)
	if !ok {
		return
	}
	sess, ok := a.openBrowse(w, r, repo)
	if !ok {
		return
	}
	defer sess.Close()

	branches, err := sess.Branches(r.Context())
	if err != nil {
		http.Error(w, "failed to list branches", http.StatusInternalServerError)
		return
	}

	branch := r.URL.Query().Get("branch")
	if branch == "" {
		branch = browse.DefaultBranch(branches)
	}
	fromHash := r.URL.Query().Get("from")

	commits, nextHash, err := sess.Log(r.Context(), branch, fromHash, logPageSize)
	if err != nil {
		if errors.Is(err, browse.ErrRefNotFound) {
			a.notFound(w, r)
			return
		}
		http.Error(w, "failed to read log", http.StatusInternalServerError)
		return
	}

	view := struct {
		basePage
		Repo     *core.Repo
		Branches []browse.Branch
		Branch   string
		Commits  []browse.CommitInfo
		NextHash string
	}{
		basePage: a.newBasePage(r, "Log — "+repo.OwnerName+"/"+repo.Name),
		Repo:     repo,
		Branches: branches,
		Branch:   branch,
		Commits:  commits,
		NextHash: nextHash,
	}
	a.render(w, http.StatusOK, "log.html", view)
}

// handleCommit renders a single commit's per-table diff summary.
func (a *app) handleCommit(w http.ResponseWriter, r *http.Request) {
	repo, _, _, ok := a.loadRepoForBrowse(w, r)
	if !ok {
		return
	}
	sess, ok := a.openBrowse(w, r, repo)
	if !ok {
		return
	}
	defer sess.Close()

	hash := chi.URLParam(r, "hash")
	summary, err := sess.CommitSummary(r.Context(), hash)
	if err != nil {
		if errors.Is(err, browse.ErrRefNotFound) {
			a.notFound(w, r)
			return
		}
		http.Error(w, "failed to read commit", http.StatusInternalServerError)
		return
	}

	view := struct {
		basePage
		Repo    *core.Repo
		Summary *browse.CommitDiff
	}{
		basePage: a.newBasePage(r, "Commit "+shortHash(hash)+" — "+repo.OwnerName+"/"+repo.Name),
		Repo:     repo,
		Summary:  summary,
	}
	a.render(w, http.StatusOK, "commit.html", view)
}

// handleTree renders the tables (with schemas) present at a ref.
func (a *app) handleTree(w http.ResponseWriter, r *http.Request) {
	repo, _, _, ok := a.loadRepoForBrowse(w, r)
	if !ok {
		return
	}
	sess, ok := a.openBrowse(w, r, repo)
	if !ok {
		return
	}
	defer sess.Close()

	ref := chi.URLParam(r, "ref")
	tables, err := sess.Tables(r.Context(), ref)
	if err != nil {
		if errors.Is(err, browse.ErrRefNotFound) {
			a.notFound(w, r)
			return
		}
		http.Error(w, "failed to read tables", http.StatusInternalServerError)
		return
	}

	view := struct {
		basePage
		Repo   *core.Repo
		Ref    string
		Tables []browse.TableInfo
	}{
		basePage: a.newBasePage(r, "Tree "+ref+" — "+repo.OwnerName+"/"+repo.Name),
		Repo:     repo,
		Ref:      ref,
		Tables:   tables,
	}
	a.render(w, http.StatusOK, "tree.html", view)
}

// handleTable renders a table's schema and a paginated page of its rows. Pages
// are selected with ?page=N (1-based).
func (a *app) handleTable(w http.ResponseWriter, r *http.Request) {
	repo, _, _, ok := a.loadRepoForBrowse(w, r)
	if !ok {
		return
	}
	sess, ok := a.openBrowse(w, r, repo)
	if !ok {
		return
	}
	defer sess.Close()

	ref := chi.URLParam(r, "ref")
	table := chi.URLParam(r, "table")

	page := 1
	if p, err := strconv.Atoi(r.URL.Query().Get("page")); err == nil && p > 1 {
		page = p
	}
	offset := (page - 1) * rowsPageSize

	rows, err := sess.Rows(r.Context(), ref, table, offset, rowsPageSize)
	if err != nil {
		if errors.Is(err, browse.ErrRefNotFound) || errors.Is(err, browse.ErrTableNotFound) {
			a.notFound(w, r)
			return
		}
		http.Error(w, "failed to read rows", http.StatusInternalServerError)
		return
	}

	totalPages := (rows.Total + rowsPageSize - 1) / rowsPageSize
	if totalPages < 1 {
		totalPages = 1
	}

	view := struct {
		basePage
		Repo       *core.Repo
		Ref        string
		Table      string
		Rows       *browse.RowPage
		Page       int
		TotalPages int
		HasPrev    bool
		HasNext    bool
	}{
		basePage:   a.newBasePage(r, table+" — "+repo.OwnerName+"/"+repo.Name),
		Repo:       repo,
		Ref:        ref,
		Table:      table,
		Rows:       rows,
		Page:       page,
		TotalPages: totalPages,
		HasPrev:    page > 1,
		HasNext:    page < totalPages,
	}
	a.render(w, http.StatusOK, "table.html", view)
}