forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
Monorepo for Tangled
fork
Configure Feed
Select the types of activity you want to include in your feed.
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
21type RepoResolver struct {
22 config *config.Config
23 enforcer *rbac.Enforcer
24 execer db.Execer
25}
26
27func New(config *config.Config, enforcer *rbac.Enforcer, execer db.Execer) *RepoResolver {
28 return &RepoResolver{config: config, enforcer: enforcer, execer: execer}
29}
30
31// NOTE: this... should not even be here. the entire package will be removed in future refactor
32func GetBaseRepoPath(r *http.Request, repo *models.Repo) string {
33 var (
34 user = chi.URLParam(r, "user")
35 name = chi.URLParam(r, "repo")
36 )
37 if user == "" || name == "" {
38 return repo.DidSlashRepo()
39 }
40 return path.Join(user, name)
41}
42
43// TODO: move this out of `RepoResolver` struct
44func (rr *RepoResolver) Resolve(r *http.Request) (*models.Repo, error) {
45 repo, ok := r.Context().Value("repo").(*models.Repo)
46 if !ok {
47 log.Println("malformed middleware: `repo` not exist in context")
48 return nil, fmt.Errorf("malformed middleware")
49 }
50
51 return repo, nil
52}
53
54// 1. [x] replace `RepoInfo` to `reporesolver.GetRepoInfo(r *http.Request, repo, user)`
55// 2. [x] remove `rr`, `CurrentDir`, `Ref` fields from `ResolvedRepo`
56// 3. [x] remove `ResolvedRepo`
57// 4. [ ] replace reporesolver to reposervice
58func (rr *RepoResolver) GetRepoInfo(r *http.Request, user *oauth.User) repoinfo.RepoInfo {
59 ownerId, ook := r.Context().Value("resolvedId").(identity.Identity)
60 repo, rok := r.Context().Value("repo").(*models.Repo)
61 if !ook || !rok {
62 log.Println("malformed request, failed to get repo from context")
63 }
64
65 // get dir/ref
66 currentDir := path.Dir(extractPathAfterRef(r.URL.EscapedPath()))
67 ref := chi.URLParam(r, "ref")
68
69 repoAt := repo.RepoAt()
70 isStarred := false
71 roles := repoinfo.RolesInRepo{}
72 if user != nil {
73 isStarred = db.GetStarStatus(rr.execer, user.Did, repoAt)
74 roles.Roles = rr.enforcer.GetPermissionsInRepo(user.Did, repo.Knot, repo.DidSlashRepo())
75 }
76
77 stats := repo.RepoStats
78 if stats == nil {
79 starCount, err := db.GetStarCount(rr.execer, repoAt)
80 if err != nil {
81 log.Println("failed to get star count for ", repoAt)
82 }
83 issueCount, err := db.GetIssueCount(rr.execer, repoAt)
84 if err != nil {
85 log.Println("failed to get issue count for ", repoAt)
86 }
87 pullCount, err := db.GetPullCount(rr.execer, repoAt)
88 if err != nil {
89 log.Println("failed to get pull count for ", repoAt)
90 }
91 stats = &models.RepoStats{
92 StarCount: starCount,
93 IssueCount: issueCount,
94 PullCount: pullCount,
95 }
96 }
97
98 var sourceRepo *models.Repo
99 var err error
100 if repo.Source != "" {
101 sourceRepo, err = db.GetRepoByAtUri(rr.execer, repo.Source)
102 if err != nil {
103 log.Println("failed to get repo by at uri", err)
104 }
105 }
106
107 repoInfo := repoinfo.RepoInfo{
108 // this is basically a models.Repo
109 OwnerDid: ownerId.DID.String(),
110 OwnerHandle: ownerId.Handle.String(),
111 Name: repo.Name,
112 Rkey: repo.Rkey,
113 Description: repo.Description,
114 Website: repo.Website,
115 Topics: repo.Topics,
116 Knot: repo.Knot,
117 Spindle: repo.Spindle,
118 Stats: *stats,
119
120 // fork repo upstream
121 Source: sourceRepo,
122
123 // page context
124 CurrentDir: currentDir,
125 Ref: ref,
126
127 // info related to the session
128 IsStarred: isStarred,
129 Roles: roles,
130 }
131
132 return repoInfo
133}
134
135// extractPathAfterRef gets the actual repository path
136// after the ref. for example:
137//
138// /@icyphox.sh/foorepo/blob/main/abc/xyz/ => abc/xyz/
139func extractPathAfterRef(fullPath string) string {
140 fullPath = strings.TrimPrefix(fullPath, "/")
141
142 // match blob/, tree/, or raw/ followed by any ref and then a slash
143 //
144 // captures everything after the final slash
145 pattern := `(?:blob|tree|raw)/[^/]+/(.*)$`
146
147 re := regexp.MustCompile(pattern)
148 matches := re.FindStringSubmatch(fullPath)
149
150 if len(matches) > 1 {
151 return matches[1]
152 }
153
154 return ""
155}