A vibe coded tangled fork which supports pijul.
1package repo
2
3import (
4 "encoding/json"
5 "fmt"
6 "net/http"
7 "net/url"
8 "strconv"
9
10 "tangled.org/core/api/tangled"
11 "tangled.org/core/appview/commitverify"
12 "tangled.org/core/appview/db"
13 "tangled.org/core/appview/models"
14 "tangled.org/core/appview/pages"
15 xrpcclient "tangled.org/core/appview/xrpcclient"
16 "tangled.org/core/types"
17
18 indigoxrpc "github.com/bluesky-social/indigo/xrpc"
19 "github.com/go-chi/chi/v5"
20 "github.com/go-git/go-git/v5/plumbing"
21)
22
23func (rp *Repo) Log(w http.ResponseWriter, r *http.Request) {
24 l := rp.logger.With("handler", "RepoLog")
25
26 f, err := rp.repoResolver.Resolve(r)
27 if err != nil {
28 l.Error("failed to fully resolve repo", "err", err)
29 return
30 }
31
32 page := 1
33 if r.URL.Query().Get("page") != "" {
34 page, err = strconv.Atoi(r.URL.Query().Get("page"))
35 if err != nil {
36 page = 1
37 }
38 }
39
40 ref := chi.URLParam(r, "ref")
41 ref, _ = url.PathUnescape(ref)
42
43 xrpcc := &indigoxrpc.Client{Host: rp.config.KnotMirror.Url}
44
45 limit := int64(60)
46 cursor := ""
47 if page > 1 {
48 // Convert page number to cursor (offset)
49 offset := (page - 1) * int(limit)
50 cursor = strconv.Itoa(offset)
51 }
52
53 xrpcBytes, err := tangled.GitTempListCommits(r.Context(), xrpcc, cursor, limit, ref, f.RepoAt().String())
54 if err != nil {
55 l.Error("failed to call XRPC repo.log", "err", err)
56 rp.pages.Error503(w)
57 return
58 }
59
60 var xrpcResp types.RepoLogResponse
61 if err := json.Unmarshal(xrpcBytes, &xrpcResp); err != nil {
62 l.Error("failed to decode XRPC response", "err", err)
63 rp.pages.Error503(w)
64 return
65 }
66
67 tagBytes, err := tangled.GitTempListTags(r.Context(), xrpcc, "", 0, f.RepoAt().String())
68 if err != nil {
69 l.Error("failed to call XRPC repo.tags", "err", err)
70 rp.pages.Error503(w)
71 return
72 }
73
74 tagMap := make(map[string][]string)
75 if tagBytes != nil {
76 var tagResp types.RepoTagsResponse
77 if err := json.Unmarshal(tagBytes, &tagResp); err == nil {
78 for _, tag := range tagResp.Tags {
79 hash := tag.Hash
80 if tag.Tag != nil {
81 hash = tag.Tag.Target.String()
82 }
83 tagMap[hash] = append(tagMap[hash], tag.Name)
84 }
85 }
86 }
87
88 branchBytes, err := tangled.GitTempListBranches(r.Context(), xrpcc, "", 0, f.RepoAt().String())
89 if err != nil {
90 l.Error("failed to call XRPC repo.branches", "err", err)
91 rp.pages.Error503(w)
92 return
93 }
94
95 if branchBytes != nil {
96 var branchResp types.RepoBranchesResponse
97 if err := json.Unmarshal(branchBytes, &branchResp); err == nil {
98 for _, branch := range branchResp.Branches {
99 tagMap[branch.Hash] = append(tagMap[branch.Hash], branch.Name)
100 }
101 }
102 }
103
104 user := rp.oauth.GetMultiAccountUser(r)
105
106 emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(xrpcResp.Commits), true)
107 if err != nil {
108 l.Error("failed to fetch email to did mapping", "err", err)
109 }
110
111 vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, xrpcResp.Commits)
112 if err != nil {
113 l.Error("failed to GetVerifiedObjectCommits", "err", err)
114 }
115
116 var shas []string
117 for _, c := range xrpcResp.Commits {
118 shas = append(shas, c.Hash.String())
119 }
120 pipelines, err := getPipelineStatuses(rp.db, f, shas)
121 if err != nil {
122 l.Error("failed to getPipelineStatuses", "err", err)
123 // non-fatal
124 }
125
126 rp.pages.RepoLog(w, pages.RepoLogParams{
127 LoggedInUser: user,
128 TagMap: tagMap,
129 RepoInfo: rp.repoResolver.GetRepoInfo(r, user),
130 RepoLogResponse: xrpcResp,
131 EmailToDid: emailToDidMap,
132 VerifiedCommits: vc,
133 Pipelines: pipelines,
134 })
135}
136
137func (rp *Repo) Commit(w http.ResponseWriter, r *http.Request) {
138 l := rp.logger.With("handler", "RepoCommit")
139
140 f, err := rp.repoResolver.Resolve(r)
141 if err != nil {
142 l.Error("failed to fully resolve repo", "err", err)
143 return
144 }
145 ref := chi.URLParam(r, "ref")
146 ref, _ = url.PathUnescape(ref)
147
148 var diffOpts types.DiffOpts
149 if d := r.URL.Query().Get("diff"); d == "split" {
150 diffOpts.Split = true
151 }
152
153 if !plumbing.IsHash(ref) {
154 rp.pages.Error404(w)
155 return
156 }
157
158 scheme := "http"
159 if !rp.config.Core.Dev {
160 scheme = "https"
161 }
162 host := fmt.Sprintf("%s://%s", scheme, f.Knot)
163 xrpcc := &indigoxrpc.Client{
164 Host: host,
165 }
166
167 repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
168 xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
169 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
170 l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
171 rp.pages.Error503(w)
172 return
173 }
174
175 var result types.RepoCommitResponse
176 if err := json.Unmarshal(xrpcBytes, &result); err != nil {
177 l.Error("failed to decode XRPC response", "err", err)
178 rp.pages.Error503(w)
179 return
180 }
181
182 emailToDidMap, err := db.GetEmailToDid(rp.db, []string{result.Diff.Commit.Committer.Email, result.Diff.Commit.Author.Email}, true)
183 if err != nil {
184 l.Error("failed to get email to did mapping", "err", err)
185 }
186
187 vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.Commit{result.Diff.Commit})
188 if err != nil {
189 l.Error("failed to GetVerifiedCommits", "err", err)
190 }
191
192 user := rp.oauth.GetMultiAccountUser(r)
193 pipelines, err := getPipelineStatuses(rp.db, f, []string{result.Diff.Commit.This})
194 if err != nil {
195 l.Error("failed to getPipelineStatuses", "err", err)
196 // non-fatal
197 }
198 var pipeline *models.Pipeline
199 if p, ok := pipelines[result.Diff.Commit.This]; ok {
200 pipeline = &p
201 }
202
203 rp.pages.RepoCommit(w, pages.RepoCommitParams{
204 LoggedInUser: user,
205 RepoInfo: rp.repoResolver.GetRepoInfo(r, user),
206 RepoCommitResponse: result,
207 EmailToDid: emailToDidMap,
208 VerifiedCommit: vc,
209 Pipeline: pipeline,
210 DiffOpts: diffOpts,
211 })
212}