Monorepo for Tangled
0

Configure Feed

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

at master 4.3 kB View raw
1package xrpc 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "path/filepath" 8 "runtime/pprof" 9 "time" 10 "unicode/utf8" 11 12 "github.com/bluesky-social/indigo/atproto/atclient" 13 "github.com/bluesky-social/indigo/atproto/syntax" 14 "tangled.org/core/api/tangled" 15 "tangled.org/core/appview/pages/markup" 16 "tangled.org/core/knotserver/git" 17 "tangled.org/core/types" 18) 19 20func (x *Xrpc) GetTree(w http.ResponseWriter, r *http.Request) { 21 var ( 22 repoQuery = r.URL.Query().Get("repo") 23 ref = r.URL.Query().Get("ref") // ref can be empty (git.Open handles this) 24 path = r.URL.Query().Get("path") // path can be empty (defaults to root) 25 ) 26 l := x.logger.With("method", "git.getTree", "repo", repoQuery, "ref", ref) 27 l.Debug("request") 28 29 repo, err := syntax.ParseATURI(repoQuery) 30 if err != nil || repo.RecordKey() == "" { 31 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", repoQuery)}) 32 return 33 } 34 35 var out *tangled.GitTempGetTree_Output 36 pprof.Do(r.Context(), pprof.Labels("repo", repo.String()), func(ctx context.Context) { 37 out, err = x.getTree(ctx, repo, ref, path) 38 }) 39 if err != nil { 40 l.Warn("local mirror failed, trying proxy", "repo", repo, "err", err) 41 if x.proxyToKnot(w, r, repo) { 42 return 43 } 44 writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "failed to get tree"}) 45 return 46 } 47 writeJson(w, http.StatusOK, out) 48} 49 50func (x *Xrpc) getTree(ctx context.Context, repo syntax.ATURI, ref, path string) (*tangled.GitTempGetTree_Output, error) { 51 repoPath, err := x.makeRepoPath(ctx, repo) 52 if err != nil { 53 return nil, fmt.Errorf("failed to resolve repo at-uri: %w", err) 54 } 55 56 gr, err := git.Open(repoPath, ref) 57 if err != nil { 58 return nil, fmt.Errorf("opening git repo: %w", err) 59 } 60 61 files, err := gr.FileTree(ctx, path) 62 if err != nil { 63 return nil, fmt.Errorf("reading file tree: %w", err) 64 } 65 66 // if any of these files are a readme candidate, pass along its blob contents too 67 var readmeFileName string 68 var readmeContents string 69 for _, file := range files { 70 if markup.IsReadmeFile(file.Name) { 71 contents, err := gr.RawContent(filepath.Join(path, file.Name)) 72 if err != nil { 73 x.logger.Error("failed to read contents of file", "path", path, "file", file.Name) 74 } 75 76 if utf8.Valid(contents) { 77 readmeFileName = file.Name 78 readmeContents = string(contents) 79 break 80 } 81 } 82 } 83 84 // convert NiceTree -> tangled.RepoTempGetTree_TreeEntry 85 treeEntries := make([]*tangled.GitTempGetTree_TreeEntry, len(files)) 86 for i, file := range files { 87 entry := &tangled.GitTempGetTree_TreeEntry{ 88 Name: file.Name, 89 Mode: file.Mode, 90 Size: file.Size, 91 } 92 if file.LastCommit != nil { 93 entry.Last_commit = &tangled.GitTempGetTree_LastCommit{ 94 Hash: file.LastCommit.Hash.String(), 95 Message: file.LastCommit.Message, 96 When: file.LastCommit.When.Format(time.RFC3339), 97 } 98 } 99 treeEntries[i] = entry 100 } 101 102 var parentPtr *string 103 if path != "" { 104 parentPtr = &path 105 } 106 107 var dotdotPtr *string 108 if path != "" { 109 dotdot := filepath.Dir(path) 110 if dotdot != "." { 111 dotdotPtr = &dotdot 112 } 113 } 114 115 // find the most recent commit across all entries for the directory-level last commit 116 var lastCommitInfo *types.LastCommitInfo 117 for _, file := range files { 118 if file.LastCommit == nil { 119 continue 120 } 121 if lastCommitInfo == nil { 122 lastCommitInfo = file.LastCommit 123 continue 124 } 125 if file.LastCommit.When.After(lastCommitInfo.When) { 126 lastCommitInfo = file.LastCommit 127 } 128 } 129 130 var lastCommit *tangled.GitTempGetTree_LastCommit 131 if lastCommitInfo != nil { 132 lastCommit = &tangled.GitTempGetTree_LastCommit{ 133 Hash: lastCommitInfo.Hash.String(), 134 Message: lastCommitInfo.Message, 135 When: lastCommitInfo.When.Format(time.RFC3339), 136 } 137 if commit, err := gr.Commit(lastCommitInfo.Hash); err == nil { 138 lastCommit.Author = &tangled.GitTempGetTree_Signature{ 139 Name: commit.Author.Name, 140 Email: commit.Author.Email, 141 } 142 } 143 } 144 145 return &tangled.GitTempGetTree_Output{ 146 Ref: ref, 147 Parent: parentPtr, 148 Dotdot: dotdotPtr, 149 Files: treeEntries, 150 LastCommit: lastCommit, 151 Readme: &tangled.GitTempGetTree_Readme{ 152 Filename: readmeFileName, 153 Contents: readmeContents, 154 }, 155 }, nil 156}