this repo has no description
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 ResolvedRepo struct {
22 models.Repo
23 OwnerId identity.Identity
24 CurrentDir string
25 Ref string
26
27 rr *RepoResolver
28}
29
30type RepoResolver struct {
31 config *config.Config
32 enforcer *rbac.Enforcer
33 execer db.Execer
34}
35
36func New(config *config.Config, enforcer *rbac.Enforcer, execer db.Execer) *RepoResolver {
37 return &RepoResolver{config: config, enforcer: enforcer, execer: execer}
38}
39
40// NOTE: this... should not even be here. the entire package will be removed in future refactor
41func GetBaseRepoPath(r *http.Request, repo *models.Repo) string {
42 var (
43 user = chi.URLParam(r, "user")
44 name = chi.URLParam(r, "repo")
45 )
46 if user == "" || name == "" {
47 return repo.DidSlashRepo()
48 }
49 return path.Join(user, name)
50}
51
52func (rr *RepoResolver) Resolve(r *http.Request) (*ResolvedRepo, error) {
53 repo, ok := r.Context().Value("repo").(*models.Repo)
54 if !ok {
55 log.Println("malformed middleware: `repo` not exist in context")
56 return nil, fmt.Errorf("malformed middleware")
57 }
58 id, ok := r.Context().Value("resolvedId").(identity.Identity)
59 if !ok {
60 log.Println("malformed middleware")
61 return nil, fmt.Errorf("malformed middleware")
62 }
63
64 currentDir := path.Dir(extractPathAfterRef(r.URL.EscapedPath()))
65 ref := chi.URLParam(r, "ref")
66
67 return &ResolvedRepo{
68 Repo: *repo,
69 OwnerId: id,
70 CurrentDir: currentDir,
71 Ref: ref,
72
73 rr: rr,
74 }, nil
75}
76
77// this function is a bit weird since it now returns RepoInfo from an entirely different
78// package. we should refactor this or get rid of RepoInfo entirely.
79func (f *ResolvedRepo) RepoInfo(user *oauth.User) repoinfo.RepoInfo {
80 repoAt := f.RepoAt()
81 isStarred := false
82 if user != nil {
83 isStarred = db.GetStarStatus(f.rr.execer, user.Did, repoAt)
84 }
85
86 stats := f.RepoStats
87 if stats == nil {
88 starCount, err := db.GetStarCount(f.rr.execer, repoAt)
89 if err != nil {
90 log.Println("failed to get star count for ", repoAt)
91 }
92 issueCount, err := db.GetIssueCount(f.rr.execer, repoAt)
93 if err != nil {
94 log.Println("failed to get issue count for ", repoAt)
95 }
96 pullCount, err := db.GetPullCount(f.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 sourceRepo, err := db.GetRepoSourceRepo(f.rr.execer, repoAt)
108 if err != nil {
109 log.Println("failed to get repo by at uri", err)
110 }
111
112 repoInfo := repoinfo.RepoInfo{
113 // this is basically a models.Repo
114 OwnerDid: f.OwnerId.DID.String(),
115 OwnerHandle: f.OwnerId.Handle.String(),
116 Name: f.Name,
117 Rkey: f.Rkey,
118 Description: f.Description,
119 Website: f.Website,
120 Topics: f.Topics,
121 Knot: f.Knot,
122 Spindle: f.Spindle,
123 Stats: *stats,
124
125 // fork repo upstream
126 Source: sourceRepo,
127
128 CurrentDir: f.CurrentDir,
129 Ref: f.Ref,
130
131 // info related to the session
132 IsStarred: isStarred,
133 Roles: f.RolesInRepo(user),
134 }
135
136 return repoInfo
137}
138
139func (f *ResolvedRepo) RolesInRepo(u *oauth.User) repoinfo.RolesInRepo {
140 if u != nil {
141 r := f.rr.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo())
142 return repoinfo.RolesInRepo{Roles: r}
143 } else {
144 return repoinfo.RolesInRepo{}
145 }
146}
147
148// extractPathAfterRef gets the actual repository path
149// after the ref. for example:
150//
151// /@icyphox.sh/foorepo/blob/main/abc/xyz/ => abc/xyz/
152func extractPathAfterRef(fullPath string) string {
153 fullPath = strings.TrimPrefix(fullPath, "/")
154
155 // match blob/, tree/, or raw/ followed by any ref and then a slash
156 //
157 // captures everything after the final slash
158 pattern := `(?:blob|tree|raw)/[^/]+/(.*)$`
159
160 re := regexp.MustCompile(pattern)
161 matches := re.FindStringSubmatch(fullPath)
162
163 if len(matches) > 1 {
164 return matches[1]
165 }
166
167 return ""
168}