Monorepo for Tangled
0
fork

Configure Feed

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

at master 187 lines 5.1 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 if repo.RepoDid != "" { 40 return repo.RepoDid 41 } 42 var ( 43 user = chi.URLParam(r, "user") 44 name = chi.URLParam(r, "repo") 45 ) 46 if user == "" || name == "" { 47 return repo.RepoIdentifier() 48 } 49 return path.Join(user, name) 50} 51 52// TODO: move this out of `RepoResolver` struct 53func (rr *RepoResolver) Resolve(r *http.Request) (*models.Repo, error) { 54 repo, ok := r.Context().Value("repo").(*models.Repo) 55 if !ok { 56 log.Println("malformed middleware: `repo` not exist in context") 57 return nil, fmt.Errorf("malformed middleware") 58 } 59 60 return repo, nil 61} 62 63// 1. [x] replace `RepoInfo` to `reporesolver.GetRepoInfo(r *http.Request, repo, user)` 64// 2. [x] remove `rr`, `CurrentDir`, `Ref` fields from `ResolvedRepo` 65// 3. [x] remove `ResolvedRepo` 66// 4. [ ] replace reporesolver to reposervice 67func (rr *RepoResolver) GetRepoInfo(r *http.Request, user *oauth.MultiAccountUser) repoinfo.RepoInfo { 68 ownerId, ook := r.Context().Value("resolvedId").(identity.Identity) 69 repo, rok := r.Context().Value("repo").(*models.Repo) 70 if !ook || !rok { 71 log.Println("malformed request, failed to get repo from context") 72 } 73 74 // get dir/ref 75 currentDir := extractCurrentDir(r.URL.EscapedPath()) 76 ref := chi.URLParam(r, "ref") 77 78 repoAt := repo.RepoAt() 79 isStarred := false 80 roles := repoinfo.RolesInRepo{} 81 if user != nil && user.Active != nil { 82 isStarred = db.GetStarStatus(rr.execer, user.Active.Did, repoAt) 83 roles.Roles = rr.enforcer.GetPermissionsInRepo(user.Active.Did, repo.Knot, repo.RepoIdentifier()) 84 } 85 86 stats := repo.RepoStats 87 if stats == nil { 88 starCount, starErr := db.GetStarCount(rr.execer, repoAt) 89 if starErr != nil { 90 log.Println("failed to get star count for ", repoAt) 91 } 92 issueCount, err := db.GetIssueCount(rr.execer, repoAt) 93 if err != nil { 94 log.Println("failed to get issue count for ", repoAt) 95 } 96 pullCount, err := db.GetPullCount(rr.execer, repoAt) 97 if err != nil { 98 log.Println("failed to get pull count for ", repoAt) 99 } 100 stats = &models.RepoStats{ 101 StarCount: starCount, 102 IssueCount: issueCount, 103 PullCount: pullCount, 104 } 105 } 106 107 var sourceRepo *models.Repo 108 var err error 109 if repo.Source != "" { 110 if strings.HasPrefix(repo.Source, "did:") { 111 sourceRepo, err = db.GetRepoByDid(rr.execer, repo.Source) 112 } else { 113 sourceRepo, err = db.GetRepoByAtUri(rr.execer, repo.Source) 114 } 115 if err != nil { 116 log.Println("failed to get source repo", err) 117 } 118 } 119 120 repoInfo := repoinfo.RepoInfo{ 121 // this is basically a models.Repo 122 OwnerDid: ownerId.DID.String(), 123 OwnerHandle: ownerId.Handle.String(), 124 Name: repo.Name, 125 Rkey: repo.Rkey, 126 Description: repo.Description, 127 Website: repo.Website, 128 Topics: repo.Topics, 129 Knot: repo.Knot, 130 Spindle: repo.Spindle, 131 Stats: *stats, 132 133 // fork repo upstream 134 Source: sourceRepo, 135 136 // page context 137 CurrentDir: currentDir, 138 Ref: ref, 139 140 // info related to the session 141 IsStarred: isStarred, 142 Roles: roles, 143 } 144 145 return repoInfo 146} 147 148// extractCurrentDir gets the current directory for markdown link resolution. 149// for blob paths, returns the parent dir. for tree paths, returns the path itself. 150// 151// /@user/repo/blob/main/docs/README.md => docs 152// /@user/repo/tree/main/docs => docs 153func extractCurrentDir(fullPath string) string { 154 fullPath = strings.TrimPrefix(fullPath, "/") 155 156 if matches := blobPattern.FindStringSubmatch(fullPath); len(matches) > 1 { 157 return path.Dir(matches[1]) 158 } 159 160 if matches := treePattern.FindStringSubmatch(fullPath); len(matches) > 1 { 161 dir := strings.TrimSuffix(matches[1], "/") 162 if dir == "" { 163 return "." 164 } 165 return dir 166 } 167 168 return "." 169} 170 171// extractPathAfterRef gets the actual repository path 172// after the ref. for example: 173// 174// /@icyphox.sh/foorepo/blob/main/abc/xyz/ => abc/xyz/ 175func extractPathAfterRef(fullPath string) string { 176 fullPath = strings.TrimPrefix(fullPath, "/") 177 178 // pathAfterRefRE matches blob/, tree/, or raw/ followed by any ref and then a slash; 179 // it captures everything after the final slash. 180 matches := pathAfterRefRE.FindStringSubmatch(fullPath) 181 182 if len(matches) > 1 { 183 return matches[1] 184 } 185 186 return "" 187}