A vibe coded tangled fork which supports pijul.
at sl/knotmirror 130 lines 4.3 kB view raw
1package repo 2 3import ( 4 "fmt" 5 "net/http" 6 "net/url" 7 "strings" 8 "time" 9 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/appview/reporesolver" 14 xrpcclient "tangled.org/core/appview/xrpcclient" 15 "tangled.org/core/types" 16 17 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 18 "github.com/go-chi/chi/v5" 19 "github.com/go-git/go-git/v5/plumbing" 20) 21 22func (rp *Repo) Tree(w http.ResponseWriter, r *http.Request) { 23 l := rp.logger.With("handler", "RepoTree") 24 f, err := rp.repoResolver.Resolve(r) 25 if err != nil { 26 l.Error("failed to fully resolve repo", "err", err) 27 return 28 } 29 ref := chi.URLParam(r, "ref") 30 ref, _ = url.PathUnescape(ref) 31 // if the tree path has a trailing slash, let's strip it 32 // so we don't 404 33 treePath := chi.URLParam(r, "*") 34 treePath, _ = url.PathUnescape(treePath) 35 treePath = strings.TrimSuffix(treePath, "/") 36 37 xrpcc := &indigoxrpc.Client{Host: rp.config.KnotMirror.Url} 38 xrpcResp, err := tangled.GitTempGetTree(r.Context(), xrpcc, treePath, ref, f.RepoAt().String()) 39 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 40 l.Error("failed to call XRPC repo.tree", "err", xrpcerr) 41 rp.pages.Error503(w) 42 return 43 } 44 // Convert XRPC response to internal types.RepoTreeResponse 45 files := make([]types.NiceTree, len(xrpcResp.Files)) 46 for i, xrpcFile := range xrpcResp.Files { 47 file := types.NiceTree{ 48 Name: xrpcFile.Name, 49 Mode: xrpcFile.Mode, 50 Size: int64(xrpcFile.Size), 51 } 52 // Convert last commit info if present 53 if xrpcFile.Last_commit != nil { 54 commitWhen, _ := time.Parse(time.RFC3339, xrpcFile.Last_commit.When) 55 file.LastCommit = &types.LastCommitInfo{ 56 Hash: plumbing.NewHash(xrpcFile.Last_commit.Hash), 57 Message: xrpcFile.Last_commit.Message, 58 When: commitWhen, 59 } 60 } 61 files[i] = file 62 } 63 result := types.RepoTreeResponse{ 64 Ref: xrpcResp.Ref, 65 Files: files, 66 } 67 if xrpcResp.Parent != nil { 68 result.Parent = *xrpcResp.Parent 69 } 70 if xrpcResp.Dotdot != nil { 71 result.DotDot = *xrpcResp.Dotdot 72 } 73 if xrpcResp.Readme != nil { 74 result.ReadmeFileName = xrpcResp.Readme.Filename 75 result.Readme = xrpcResp.Readme.Contents 76 } 77 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 78 // redirects tree paths trying to access a blob; in this case the result.Files is unpopulated, 79 // so we can safely redirect to the "parent" (which is the same file). 80 if len(result.Files) == 0 && result.Parent == treePath { 81 redirectTo := fmt.Sprintf("/%s/blob/%s/%s", ownerSlashRepo, url.PathEscape(ref), result.Parent) 82 http.Redirect(w, r, redirectTo, http.StatusFound) 83 return 84 } 85 user := rp.oauth.GetMultiAccountUser(r) 86 var breadcrumbs [][]string 87 breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))}) 88 if treePath != "" { 89 for idx, elem := range strings.Split(treePath, "/") { 90 breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))}) 91 } 92 } 93 sortFiles(result.Files) 94 95 // Get email to DID mapping for commit author 96 var emails []string 97 if xrpcResp.LastCommit != nil && xrpcResp.LastCommit.Author != nil { 98 emails = append(emails, xrpcResp.LastCommit.Author.Email) 99 } 100 emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true) 101 if err != nil { 102 l.Error("failed to get email to did mapping", "err", err) 103 emailToDidMap = make(map[string]string) 104 } 105 106 var lastCommitInfo *types.LastCommitInfo 107 if xrpcResp.LastCommit != nil { 108 when, _ := time.Parse(time.RFC3339, xrpcResp.LastCommit.When) 109 lastCommitInfo = &types.LastCommitInfo{ 110 Hash: plumbing.NewHash(xrpcResp.LastCommit.Hash), 111 Message: xrpcResp.LastCommit.Message, 112 When: when, 113 } 114 if xrpcResp.LastCommit.Author != nil { 115 lastCommitInfo.Author.Name = xrpcResp.LastCommit.Author.Name 116 lastCommitInfo.Author.Email = xrpcResp.LastCommit.Author.Email 117 lastCommitInfo.Author.When, _ = time.Parse(time.RFC3339, xrpcResp.LastCommit.Author.When) 118 } 119 } 120 121 rp.pages.RepoTree(w, pages.RepoTreeParams{ 122 LoggedInUser: user, 123 BreadCrumbs: breadcrumbs, 124 Path: treePath, 125 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 126 EmailToDid: emailToDidMap, 127 LastCommitInfo: lastCommitInfo, 128 RepoTreeResponse: result, 129 }) 130}