forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
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 scheme := "http"
44 if !rp.config.Core.Dev {
45 scheme = "https"
46 }
47 host := fmt.Sprintf("%s://%s", scheme, f.Knot)
48 xrpcc := &indigoxrpc.Client{
49 Host: host,
50 }
51
52 limit := int64(60)
53 cursor := ""
54 if page > 1 {
55 // Convert page number to cursor (offset)
56 offset := (page - 1) * int(limit)
57 cursor = strconv.Itoa(offset)
58 }
59
60 repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
61 xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
62 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
63 l.Error("failed to call XRPC repo.log", "err", xrpcerr)
64 rp.pages.Error503(w)
65 return
66 }
67
68 var xrpcResp types.RepoLogResponse
69 if err := json.Unmarshal(xrpcBytes, &xrpcResp); err != nil {
70 l.Error("failed to decode XRPC response", "err", err)
71 rp.pages.Error503(w)
72 return
73 }
74
75 tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
76 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
77 l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
78 rp.pages.Error503(w)
79 return
80 }
81
82 tagMap := make(map[string][]string)
83 if tagBytes != nil {
84 var tagResp types.RepoTagsResponse
85 if err := json.Unmarshal(tagBytes, &tagResp); err == nil {
86 for _, tag := range tagResp.Tags {
87 hash := tag.Hash
88 if tag.Tag != nil {
89 hash = tag.Tag.Target.String()
90 }
91 tagMap[hash] = append(tagMap[hash], tag.Name)
92 }
93 }
94 }
95
96 branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
97 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
98 l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
99 rp.pages.Error503(w)
100 return
101 }
102
103 if branchBytes != nil {
104 var branchResp types.RepoBranchesResponse
105 if err := json.Unmarshal(branchBytes, &branchResp); err == nil {
106 for _, branch := range branchResp.Branches {
107 tagMap[branch.Hash] = append(tagMap[branch.Hash], branch.Name)
108 }
109 }
110 }
111
112 user := rp.oauth.GetUser(r)
113
114 emailToDidMap, err := db.GetEmailToDid(rp.db, uniqueEmails(xrpcResp.Commits), true)
115 if err != nil {
116 l.Error("failed to fetch email to did mapping", "err", err)
117 }
118
119 vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, xrpcResp.Commits)
120 if err != nil {
121 l.Error("failed to GetVerifiedObjectCommits", "err", err)
122 }
123
124 var shas []string
125 for _, c := range xrpcResp.Commits {
126 shas = append(shas, c.Hash.String())
127 }
128 pipelines, err := getPipelineStatuses(rp.db, f, shas)
129 if err != nil {
130 l.Error("failed to getPipelineStatuses", "err", err)
131 // non-fatal
132 }
133
134 rp.pages.RepoLog(w, pages.RepoLogParams{
135 LoggedInUser: user,
136 TagMap: tagMap,
137 RepoInfo: rp.repoResolver.GetRepoInfo(r, user),
138 RepoLogResponse: xrpcResp,
139 EmailToDid: emailToDidMap,
140 VerifiedCommits: vc,
141 Pipelines: pipelines,
142 })
143}
144
145func (rp *Repo) Commit(w http.ResponseWriter, r *http.Request) {
146 l := rp.logger.With("handler", "RepoCommit")
147
148 f, err := rp.repoResolver.Resolve(r)
149 if err != nil {
150 l.Error("failed to fully resolve repo", "err", err)
151 return
152 }
153 ref := chi.URLParam(r, "ref")
154 ref, _ = url.PathUnescape(ref)
155
156 var diffOpts types.DiffOpts
157 if d := r.URL.Query().Get("diff"); d == "split" {
158 diffOpts.Split = true
159 }
160
161 if !plumbing.IsHash(ref) {
162 rp.pages.Error404(w)
163 return
164 }
165
166 scheme := "http"
167 if !rp.config.Core.Dev {
168 scheme = "https"
169 }
170 host := fmt.Sprintf("%s://%s", scheme, f.Knot)
171 xrpcc := &indigoxrpc.Client{
172 Host: host,
173 }
174
175 repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
176 xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
177 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
178 l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
179 rp.pages.Error503(w)
180 return
181 }
182
183 var result types.RepoCommitResponse
184 if err := json.Unmarshal(xrpcBytes, &result); err != nil {
185 l.Error("failed to decode XRPC response", "err", err)
186 rp.pages.Error503(w)
187 return
188 }
189
190 emailToDidMap, err := db.GetEmailToDid(rp.db, []string{result.Diff.Commit.Committer.Email, result.Diff.Commit.Author.Email}, true)
191 if err != nil {
192 l.Error("failed to get email to did mapping", "err", err)
193 }
194
195 vc, err := commitverify.GetVerifiedCommits(rp.db, emailToDidMap, []types.Commit{result.Diff.Commit})
196 if err != nil {
197 l.Error("failed to GetVerifiedCommits", "err", err)
198 }
199
200 user := rp.oauth.GetUser(r)
201 pipelines, err := getPipelineStatuses(rp.db, f, []string{result.Diff.Commit.This})
202 if err != nil {
203 l.Error("failed to getPipelineStatuses", "err", err)
204 // non-fatal
205 }
206 var pipeline *models.Pipeline
207 if p, ok := pipelines[result.Diff.Commit.This]; ok {
208 pipeline = &p
209 }
210
211 rp.pages.RepoCommit(w, pages.RepoCommitParams{
212 LoggedInUser: user,
213 RepoInfo: rp.repoResolver.GetRepoInfo(r, user),
214 RepoCommitResponse: result,
215 EmailToDid: emailToDidMap,
216 VerifiedCommit: vc,
217 Pipeline: pipeline,
218 DiffOpts: diffOpts,
219 })
220}