A vibe coded tangled fork which supports pijul.
at 3fd6663123a1d61875454c5a96e47069eb4c0ef0 180 lines 4.9 kB view raw
1package reporesolver 2 3import ( 4 "fmt" 5 "log" 6 "net/http" 7 "path" 8 "regexp" 9 "strings" 10 11 "github.com/bluesky-social/indigo/atproto/identity" 12 "github.com/go-chi/chi/v5" 13 "tangled.org/core/appview/config" 14 "tangled.org/core/appview/db" 15 "tangled.org/core/appview/models" 16 "tangled.org/core/appview/oauth" 17 "tangled.org/core/appview/pages/repoinfo" 18 "tangled.org/core/rbac" 19) 20 21var ( 22 blobPattern = regexp.MustCompile(`blob/[^/]+/(.*)$`) 23 treePattern = regexp.MustCompile(`tree/[^/]+/(.*)$`) 24 pathAfterRefRE = regexp.MustCompile(`(?:blob|tree|raw)/[^/]+/(.*)$`) 25) 26 27type RepoResolver struct { 28 config *config.Config 29 enforcer *rbac.Enforcer 30 execer db.Execer 31} 32 33func New(config *config.Config, enforcer *rbac.Enforcer, execer db.Execer) *RepoResolver { 34 return &RepoResolver{config: config, enforcer: enforcer, execer: execer} 35} 36 37// NOTE: this... should not even be here. the entire package will be removed in future refactor 38func GetBaseRepoPath(r *http.Request, repo *models.Repo) string { 39 var ( 40 user = chi.URLParam(r, "user") 41 name = chi.URLParam(r, "repo") 42 ) 43 if user == "" || name == "" { 44 return repo.DidSlashRepo() 45 } 46 return path.Join(user, name) 47} 48 49// TODO: move this out of `RepoResolver` struct 50func (rr *RepoResolver) Resolve(r *http.Request) (*models.Repo, error) { 51 repo, ok := r.Context().Value("repo").(*models.Repo) 52 if !ok { 53 log.Println("malformed middleware: `repo` not exist in context") 54 return nil, fmt.Errorf("malformed middleware") 55 } 56 57 return repo, nil 58} 59 60// 1. [x] replace `RepoInfo` to `reporesolver.GetRepoInfo(r *http.Request, repo, user)` 61// 2. [x] remove `rr`, `CurrentDir`, `Ref` fields from `ResolvedRepo` 62// 3. [x] remove `ResolvedRepo` 63// 4. [ ] replace reporesolver to reposervice 64func (rr *RepoResolver) GetRepoInfo(r *http.Request, user *oauth.MultiAccountUser) repoinfo.RepoInfo { 65 ownerId, ook := r.Context().Value("resolvedId").(identity.Identity) 66 repo, rok := r.Context().Value("repo").(*models.Repo) 67 if !ook || !rok { 68 log.Println("malformed request, failed to get repo from context") 69 } 70 71 // get dir/ref 72 currentDir := extractCurrentDir(r.URL.EscapedPath()) 73 ref := chi.URLParam(r, "ref") 74 75 repoAt := repo.RepoAt() 76 isStarred := false 77 roles := repoinfo.RolesInRepo{} 78 if user != nil && user.Active != nil { 79 isStarred = db.GetStarStatus(rr.execer, user.Active.Did, repoAt) 80 roles.Roles = rr.enforcer.GetPermissionsInRepo(user.Active.Did, repo.Knot, repo.DidSlashRepo()) 81 } 82 83 stats := repo.RepoStats 84 if stats == nil { 85 starCount, err := db.GetStarCount(rr.execer, repoAt) 86 if err != nil { 87 log.Println("failed to get star count for ", repoAt) 88 } 89 issueCount, err := db.GetIssueCount(rr.execer, repoAt) 90 if err != nil { 91 log.Println("failed to get issue count for ", repoAt) 92 } 93 pullCount, err := db.GetPullCount(rr.execer, repoAt) 94 if err != nil { 95 log.Println("failed to get pull count for ", repoAt) 96 } 97 stats = &models.RepoStats{ 98 StarCount: starCount, 99 IssueCount: issueCount, 100 PullCount: pullCount, 101 } 102 } 103 104 var sourceRepo *models.Repo 105 var err error 106 if repo.Source != "" { 107 sourceRepo, err = db.GetRepoByAtUri(rr.execer, repo.Source) 108 if err != nil { 109 log.Println("failed to get repo by at uri", err) 110 } 111 } 112 113 repoInfo := repoinfo.RepoInfo{ 114 // this is basically a models.Repo 115 OwnerDid: ownerId.DID.String(), 116 OwnerHandle: ownerId.Handle.String(), 117 Name: repo.Name, 118 Rkey: repo.Rkey, 119 Description: repo.Description, 120 Website: repo.Website, 121 Topics: repo.Topics, 122 Knot: repo.Knot, 123 Spindle: repo.Spindle, 124 Stats: *stats, 125 126 // fork repo upstream 127 Source: sourceRepo, 128 129 // page context 130 CurrentDir: currentDir, 131 Ref: ref, 132 133 // info related to the session 134 IsStarred: isStarred, 135 Roles: roles, 136 } 137 138 return repoInfo 139} 140 141// extractCurrentDir gets the current directory for markdown link resolution. 142// for blob paths, returns the parent dir. for tree paths, returns the path itself. 143// 144// /@user/repo/blob/main/docs/README.md => docs 145// /@user/repo/tree/main/docs => docs 146func extractCurrentDir(fullPath string) string { 147 fullPath = strings.TrimPrefix(fullPath, "/") 148 149 if matches := blobPattern.FindStringSubmatch(fullPath); len(matches) > 1 { 150 return path.Dir(matches[1]) 151 } 152 153 if matches := treePattern.FindStringSubmatch(fullPath); len(matches) > 1 { 154 dir := strings.TrimSuffix(matches[1], "/") 155 if dir == "" { 156 return "." 157 } 158 return dir 159 } 160 161 return "." 162} 163 164// extractPathAfterRef gets the actual repository path 165// after the ref. for example: 166// 167// /@icyphox.sh/foorepo/blob/main/abc/xyz/ => abc/xyz/ 168func extractPathAfterRef(fullPath string) string { 169 fullPath = strings.TrimPrefix(fullPath, "/") 170 171 // pathAfterRefRE matches blob/, tree/, or raw/ followed by any ref and then a slash; 172 // it captures everything after the final slash. 173 matches := pathAfterRefRE.FindStringSubmatch(fullPath) 174 175 if len(matches) > 1 { 176 return matches[1] 177 } 178 179 return "" 180}