Monorepo for Tangled tangled.org
1

Configure Feed

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

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