This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

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