+11
appview/db/repos.go
+11
appview/db/repos.go
···
411
411
return nullableSource.String, nil
412
412
}
413
413
414
+
func GetRepoSourceRepo(e Execer, repoAt syntax.ATURI) (*models.Repo, error) {
415
+
source, err := GetRepoSource(e, repoAt)
416
+
if source == "" || errors.Is(err, sql.ErrNoRows) {
417
+
return nil, nil
418
+
}
419
+
if err != nil {
420
+
return nil, err
421
+
}
422
+
return GetRepoByAtUri(e, source)
423
+
}
424
+
414
425
func GetForksByDid(e Execer, did string) ([]models.Repo, error) {
415
426
var repos []models.Repo
416
427
+8
-17
appview/issues/issues.go
+8
-17
appview/issues/issues.go
···
7
7
"fmt"
8
8
"log/slog"
9
9
"net/http"
10
-
"slices"
11
10
"time"
12
11
13
12
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
286
285
return
287
286
}
288
287
289
-
collaborators, err := f.Collaborators(r.Context())
290
-
if err != nil {
291
-
l.Error("failed to fetch repo collaborators", "err", err)
292
-
}
293
-
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
294
-
return user.Did == collab.Did
295
-
})
288
+
roles := f.RolesInRepo(user)
289
+
isRepoOwner := roles.IsOwner()
290
+
isCollaborator := roles.IsCollaborator()
296
291
isIssueOwner := user.Did == issue.Did
297
292
298
293
// TODO: make this more granular
299
-
if isIssueOwner || isCollaborator {
294
+
if isIssueOwner || isRepoOwner || isCollaborator {
300
295
err = db.CloseIssues(
301
296
rp.db,
302
297
db.FilterEq("id", issue.Id),
···
338
333
return
339
334
}
340
335
341
-
collaborators, err := f.Collaborators(r.Context())
342
-
if err != nil {
343
-
l.Error("failed to fetch repo collaborators", "err", err)
344
-
}
345
-
isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool {
346
-
return user.Did == collab.Did
347
-
})
336
+
roles := f.RolesInRepo(user)
337
+
isRepoOwner := roles.IsOwner()
338
+
isCollaborator := roles.IsCollaborator()
348
339
isIssueOwner := user.Did == issue.Did
349
340
350
-
if isCollaborator || isIssueOwner {
341
+
if isCollaborator || isRepoOwner || isIssueOwner {
351
342
err := db.ReopenIssues(
352
343
rp.db,
353
344
db.FilterEq("id", issue.Id),
+4
-2
appview/pulls/pulls.go
+4
-2
appview/pulls/pulls.go
···
877
877
}
878
878
879
879
// Determine PR type based on input parameters
880
-
isPushAllowed := f.RepoInfo(user).Roles.IsPushAllowed()
880
+
roles := f.RolesInRepo(user)
881
+
isPushAllowed := roles.IsPushAllowed()
881
882
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
882
883
isForkBased := fromFork != "" && sourceBranch != ""
883
884
isPatchBased := patch != "" && !isBranchBased && !isForkBased
···
1673
1674
return
1674
1675
}
1675
1676
1676
-
if !f.RepoInfo(user).Roles.IsPushAllowed() {
1677
+
roles := f.RolesInRepo(user)
1678
+
if !roles.IsPushAllowed() {
1677
1679
log.Println("unauthorized user")
1678
1680
w.WriteHeader(http.StatusUnauthorized)
1679
1681
return
+30
-2
appview/repo/settings.go
+30
-2
appview/repo/settings.go
···
10
10
11
11
"tangled.org/core/api/tangled"
12
12
"tangled.org/core/appview/db"
13
+
"tangled.org/core/appview/models"
13
14
"tangled.org/core/appview/oauth"
14
15
"tangled.org/core/appview/pages"
15
16
xrpcclient "tangled.org/core/appview/xrpcclient"
···
271
272
f, err := rp.repoResolver.Resolve(r)
272
273
user := rp.oauth.GetUser(r)
273
274
274
-
repoCollaborators, err := f.Collaborators(r.Context())
275
+
collaborators, err := func(repo *models.Repo) ([]pages.Collaborator, error) {
276
+
repoCollaborators, err := rp.enforcer.E.GetImplicitUsersForResourceByDomain(repo.DidSlashRepo(), repo.Knot)
277
+
if err != nil {
278
+
return nil, err
279
+
}
280
+
var collaborators []pages.Collaborator
281
+
for _, item := range repoCollaborators {
282
+
// currently only two roles: owner and member
283
+
var role string
284
+
switch item[3] {
285
+
case "repo:owner":
286
+
role = "owner"
287
+
case "repo:collaborator":
288
+
role = "collaborator"
289
+
default:
290
+
continue
291
+
}
292
+
293
+
did := item[0]
294
+
295
+
c := pages.Collaborator{
296
+
Did: did,
297
+
Role: role,
298
+
}
299
+
collaborators = append(collaborators, c)
300
+
}
301
+
return collaborators, nil
302
+
}(&f.Repo)
275
303
if err != nil {
276
304
l.Error("failed to get collaborators", "err", err)
277
305
}
···
281
309
RepoInfo: f.RepoInfo(user),
282
310
Tabs: settingsTabs,
283
311
Tab: "access",
284
-
Collaborators: repoCollaborators,
312
+
Collaborators: collaborators,
285
313
})
286
314
}
287
315
+36
-77
appview/reporesolver/resolver.go
+36
-77
appview/reporesolver/resolver.go
···
1
1
package reporesolver
2
2
3
3
import (
4
-
"context"
5
-
"database/sql"
6
-
"errors"
7
4
"fmt"
8
5
"log"
9
6
"net/http"
···
17
14
"tangled.org/core/appview/db"
18
15
"tangled.org/core/appview/models"
19
16
"tangled.org/core/appview/oauth"
20
-
"tangled.org/core/appview/pages"
21
17
"tangled.org/core/appview/pages/repoinfo"
22
-
"tangled.org/core/idresolver"
23
18
"tangled.org/core/rbac"
24
19
)
25
20
···
33
28
}
34
29
35
30
type RepoResolver struct {
36
-
config *config.Config
37
-
enforcer *rbac.Enforcer
38
-
idResolver *idresolver.Resolver
39
-
execer db.Execer
31
+
config *config.Config
32
+
enforcer *rbac.Enforcer
33
+
execer db.Execer
40
34
}
41
35
42
-
func New(config *config.Config, enforcer *rbac.Enforcer, resolver *idresolver.Resolver, execer db.Execer) *RepoResolver {
43
-
return &RepoResolver{config: config, enforcer: enforcer, idResolver: resolver, execer: execer}
36
+
func New(config *config.Config, enforcer *rbac.Enforcer, execer db.Execer) *RepoResolver {
37
+
return &RepoResolver{config: config, enforcer: enforcer, execer: execer}
44
38
}
45
39
46
40
// NOTE: this... should not even be here. the entire package will be removed in future refactor
···
80
74
}, nil
81
75
}
82
76
83
-
func (f *ResolvedRepo) Collaborators(ctx context.Context) ([]pages.Collaborator, error) {
84
-
repoCollaborators, err := f.rr.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
85
-
if err != nil {
86
-
return nil, err
87
-
}
88
-
89
-
var collaborators []pages.Collaborator
90
-
for _, item := range repoCollaborators {
91
-
// currently only two roles: owner and member
92
-
var role string
93
-
switch item[3] {
94
-
case "repo:owner":
95
-
role = "owner"
96
-
case "repo:collaborator":
97
-
role = "collaborator"
98
-
default:
99
-
continue
100
-
}
101
-
102
-
did := item[0]
103
-
104
-
c := pages.Collaborator{
105
-
Did: did,
106
-
Role: role,
107
-
}
108
-
collaborators = append(collaborators, c)
109
-
}
110
-
111
-
return collaborators, nil
112
-
}
113
-
114
77
// this function is a bit weird since it now returns RepoInfo from an entirely different
115
78
// package. we should refactor this or get rid of RepoInfo entirely.
116
79
func (f *ResolvedRepo) RepoInfo(user *oauth.User) repoinfo.RepoInfo {
···
120
83
isStarred = db.GetStarStatus(f.rr.execer, user.Did, repoAt)
121
84
}
122
85
123
-
starCount, err := db.GetStarCount(f.rr.execer, repoAt)
124
-
if err != nil {
125
-
log.Println("failed to get star count for ", repoAt)
126
-
}
127
-
issueCount, err := db.GetIssueCount(f.rr.execer, repoAt)
128
-
if err != nil {
129
-
log.Println("failed to get issue count for ", repoAt)
130
-
}
131
-
pullCount, err := db.GetPullCount(f.rr.execer, repoAt)
132
-
if err != nil {
133
-
log.Println("failed to get issue count for ", repoAt)
134
-
}
135
-
source, err := db.GetRepoSource(f.rr.execer, repoAt)
136
-
if errors.Is(err, sql.ErrNoRows) {
137
-
source = ""
138
-
} else if err != nil {
139
-
log.Println("failed to get repo source for ", repoAt, err)
140
-
}
141
-
142
-
var sourceRepo *models.Repo
143
-
if source != "" {
144
-
sourceRepo, err = db.GetRepoByAtUri(f.rr.execer, source)
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)
145
97
if err != nil {
146
-
log.Println("failed to get repo by at uri", err)
98
+
log.Println("failed to get pull count for ", repoAt)
99
+
}
100
+
stats = &models.RepoStats{
101
+
StarCount: starCount,
102
+
IssueCount: issueCount,
103
+
PullCount: pullCount,
147
104
}
148
105
}
149
106
150
-
knot := f.Knot
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
+
}
151
111
152
112
repoInfo := repoinfo.RepoInfo{
113
+
// this is basically a models.Repo
153
114
OwnerDid: f.OwnerId.DID.String(),
154
115
OwnerHandle: f.OwnerId.Handle.String(),
155
116
Name: f.Name,
···
157
118
Description: f.Description,
158
119
Website: f.Website,
159
120
Topics: f.Topics,
160
-
IsStarred: isStarred,
161
-
Knot: knot,
121
+
Knot: f.Knot,
162
122
Spindle: f.Spindle,
163
-
Roles: f.RolesInRepo(user),
164
-
Stats: models.RepoStats{
165
-
StarCount: starCount,
166
-
IssueCount: issueCount,
167
-
PullCount: pullCount,
168
-
},
123
+
Stats: *stats,
124
+
125
+
// fork repo upstream
126
+
Source: sourceRepo,
127
+
169
128
CurrentDir: f.CurrentDir,
170
129
Ref: f.Ref,
171
-
}
172
130
173
-
if sourceRepo != nil {
174
-
repoInfo.Source = sourceRepo
131
+
// info related to the session
132
+
IsStarred: isStarred,
133
+
Roles: f.RolesInRepo(user),
175
134
}
176
135
177
136
return repoInfo
+1
-1
appview/state/state.go
+1
-1
appview/state/state.go
+8
rbac/rbac.go
+8
rbac/rbac.go
···
285
285
return e.E.Enforce(user, domain, repo, "repo:delete")
286
286
}
287
287
288
+
func (e *Enforcer) IsRepoOwner(user, domain, repo string) (bool, error) {
289
+
return e.E.Enforce(user, domain, repo, "repo:owner")
290
+
}
291
+
292
+
func (e *Enforcer) IsRepoCollaborator(user, domain, repo string) (bool, error) {
293
+
return e.E.Enforce(user, domain, repo, "repo:collaborator")
294
+
}
295
+
288
296
func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) {
289
297
return e.E.Enforce(user, domain, repo, "repo:push")
290
298
}