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