tenseleyflow/shithub / b01b43c

Browse files

web/handlers/repo: render verified badge on commit list + single-commit pages

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
b01b43cfcd0cb0ac66252828abfe1ec94441d750
Parents
645d4ff
Tree
6799b4f

3 changed files

StatusFile+-
M internal/web/handlers/repo/history.go 52 19
M internal/web/templates/repo/commit.html 4 1
M internal/web/templates/repo/commits.html 1 0
internal/web/handlers/repo/history.gomodified
@@ -21,6 +21,7 @@ import (
2121
 	diffsource "github.com/tenseleyFlow/shithub/internal/repos/diff/source"
2222
 	"github.com/tenseleyFlow/shithub/internal/repos/git"
2323
 	"github.com/tenseleyFlow/shithub/internal/repos/identity"
24
+	"github.com/tenseleyFlow/shithub/internal/repos/sigverify"
2425
 	"github.com/tenseleyFlow/shithub/internal/web/middleware"
2526
 )
2627
 
@@ -113,6 +114,24 @@ func (h *Handlers) commitsList(w http.ResponseWriter, r *http.Request) {
113114
 		rows = append(rows, newCommitRow(c, resolver.Resolve(r.Context(), c.AuthorEmail)))
114115
 	}
115116
 
117
+	// Batch-load signature verification cache for the page. Failure
118
+	// is non-fatal — the rows default to UnsignedView and the page
119
+	// renders without badges.
120
+	if len(rows) > 0 {
121
+		oids := make([]string, len(rows))
122
+		for i := range rows {
123
+			oids[i] = rows[i].Commit.OID
124
+		}
125
+		verifications, vErr := sigverify.LoadViewsForOIDs(r.Context(), h.d.Pool, row.ID, oids)
126
+		if vErr != nil {
127
+			h.d.Logger.WarnContext(r.Context(), "commits: load verifications", "error", vErr, "repo_id", row.ID)
128
+		} else {
129
+			for i := range rows {
130
+				rows[i].Verification = sigverify.LookupView(verifications, rows[i].Commit.OID)
131
+			}
132
+		}
133
+	}
134
+
116135
 	filterValues := commitFilterValues(pathFilter, authorFilter, sinceRaw, untilRaw)
117136
 	olderHref := ""
118137
 	if len(commits) == perPage {
@@ -221,18 +240,30 @@ func (h *Handlers) commitView(w http.ResponseWriter, r *http.Request) {
221240
 		}
222241
 	}
223242
 
243
+	// Load signature verification cache row for this commit. Failure
244
+	// is non-fatal — falls back to UnsignedView so the page renders
245
+	// without a badge.
246
+	verification, vErr := sigverify.LoadView(r.Context(), h.d.Pool, row.ID, detail.OID)
247
+	if vErr != nil {
248
+		// pgx.ErrNoRows is normal (commit hasn't been verified yet);
249
+		// LoadView already returns UnsignedView for that case. Other
250
+		// errors get logged but the page still renders.
251
+		h.d.Logger.WarnContext(r.Context(), "commit: load verification", "error", vErr, "repo_id", row.ID, "sha", detail.OID)
252
+	}
253
+
224254
 	h.d.Render.RenderPage(w, r, "repo/commit", map[string]any{
225
-		"Title":     detail.Subject + " · " + row.Name,
226
-		"CSRFToken": middleware.CSRFTokenForRequest(r),
227
-		"Owner":     owner.Username,
228
-		"Repo":      row,
229
-		"Detail":    detail,
230
-		"Author":    author,
231
-		"Committer": committer,
232
-		"BodyHTML":  template.HTML(linkifyCommitBody(detail.Body)), //nolint:gosec // escaped inside
233
-		"DiffHTML":  diffHTML,
234
-		"DiffMode":  string(mode),
235
-		"HideWS":    hideWS,
255
+		"Title":        detail.Subject + " · " + row.Name,
256
+		"CSRFToken":    middleware.CSRFTokenForRequest(r),
257
+		"Owner":        owner.Username,
258
+		"Repo":         row,
259
+		"Detail":       detail,
260
+		"Author":       author,
261
+		"Committer":    committer,
262
+		"BodyHTML":     template.HTML(linkifyCommitBody(detail.Body)), //nolint:gosec // escaped inside
263
+		"DiffHTML":     diffHTML,
264
+		"DiffMode":     string(mode),
265
+		"HideWS":       hideWS,
266
+		"Verification": verification,
236267
 	})
237268
 }
238269
 
@@ -306,18 +337,20 @@ func (h *Handlers) commitsAtom(w http.ResponseWriter, r *http.Request) {
306337
 // git data so templates can render avatars and profile links without
307338
 // re-running the resolver.
308339
 type commitRow struct {
309
-	Commit      git.Commit
310
-	Author      identity.Resolved
311
-	AuthorLabel string
312
-	AuthorHref  string
340
+	Commit       git.Commit
341
+	Author       identity.Resolved
342
+	AuthorLabel  string
343
+	AuthorHref   string
344
+	Verification sigverify.View
313345
 }
314346
 
315347
 func newCommitRow(c git.Commit, author identity.Resolved) commitRow {
316348
 	return commitRow{
317
-		Commit:      c,
318
-		Author:      author,
319
-		AuthorLabel: commitAuthorLabel(c, author),
320
-		AuthorHref:  commitAuthorHref(author),
349
+		Commit:       c,
350
+		Author:       author,
351
+		AuthorLabel:  commitAuthorLabel(c, author),
352
+		AuthorHref:   commitAuthorHref(author),
353
+		Verification: sigverify.UnsignedView(),
321354
 	}
322355
 }
323356
 
internal/web/templates/repo/commit.htmlmodified
@@ -10,7 +10,10 @@
1010
   </header>
1111
 
1212
   <article class="shithub-commit-meta">
13
-    <h2 class="shithub-commit-subject">{{ .Detail.Subject }}</h2>
13
+    <h2 class="shithub-commit-subject">
14
+      {{ .Detail.Subject }}
15
+      {{ template "verified-badge" .Verification }}
16
+    </h2>
1417
     {{ if .Detail.Body }}<div class="shithub-commit-body">{{ .BodyHTML }}</div>{{ end }}
1518
 
1619
     <div class="shithub-commit-people">
internal/web/templates/repo/commits.htmlmodified
@@ -171,6 +171,7 @@
171171
               </div>
172172
             </div>
173173
             <div class="shithub-commit-row-actions">
174
+              {{ template "verified-badge" .Verification }}
174175
               <a class="shithub-commits-sha" href="/{{ $.Owner }}/{{ $.Repo.Name }}/commit/{{ .Commit.OID }}">{{ .Commit.ShortOID }}</a>
175176
               <button type="button" class="shithub-commit-icon-action" title="Copy full SHA" aria-label="Copy full SHA" data-commit-copy="{{ .Commit.OID }}">{{ octicon "copy" }}</button>
176177
               <a class="shithub-commit-icon-action" href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Commit.OID }}" title="Browse repository at this commit" aria-label="Browse repository at this commit">{{ octicon "code" }}</a>