+11
appview/db/repos.go
+11
appview/db/repos.go
···
411
return nullableSource.String, nil
412
}
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
+
425
func GetForksByDid(e Execer, did string) ([]models.Repo, error) {
426
var repos []models.Repo
427
+8
-17
appview/issues/issues.go
+8
-17
appview/issues/issues.go
···
7
"fmt"
8
"log/slog"
9
"net/http"
10
-
"slices"
11
"time"
12
13
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
286
return
287
}
288
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
-
})
296
isIssueOwner := user.Did == issue.Did
297
298
// TODO: make this more granular
299
-
if isIssueOwner || isCollaborator {
300
err = db.CloseIssues(
301
rp.db,
302
db.FilterEq("id", issue.Id),
···
338
return
339
}
340
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
-
})
348
isIssueOwner := user.Did == issue.Did
349
350
-
if isCollaborator || isIssueOwner {
351
err := db.ReopenIssues(
352
rp.db,
353
db.FilterEq("id", issue.Id),
···
7
"fmt"
8
"log/slog"
9
"net/http"
10
"time"
11
12
comatproto "github.com/bluesky-social/indigo/api/atproto"
···
285
return
286
}
287
288
+
roles := f.RolesInRepo(user)
289
+
isRepoOwner := roles.IsOwner()
290
+
isCollaborator := roles.IsCollaborator()
291
isIssueOwner := user.Did == issue.Did
292
293
// TODO: make this more granular
294
+
if isIssueOwner || isRepoOwner || isCollaborator {
295
err = db.CloseIssues(
296
rp.db,
297
db.FilterEq("id", issue.Id),
···
333
return
334
}
335
336
+
roles := f.RolesInRepo(user)
337
+
isRepoOwner := roles.IsOwner()
338
+
isCollaborator := roles.IsCollaborator()
339
isIssueOwner := user.Did == issue.Did
340
341
+
if isCollaborator || isRepoOwner || isIssueOwner {
342
err := db.ReopenIssues(
343
rp.db,
344
db.FilterEq("id", issue.Id),
+4
-2
appview/pulls/pulls.go
+4
-2
appview/pulls/pulls.go
···
877
}
878
879
// Determine PR type based on input parameters
880
-
isPushAllowed := f.RepoInfo(user).Roles.IsPushAllowed()
881
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
882
isForkBased := fromFork != "" && sourceBranch != ""
883
isPatchBased := patch != "" && !isBranchBased && !isForkBased
···
1673
return
1674
}
1675
1676
-
if !f.RepoInfo(user).Roles.IsPushAllowed() {
1677
log.Println("unauthorized user")
1678
w.WriteHeader(http.StatusUnauthorized)
1679
return
···
877
}
878
879
// Determine PR type based on input parameters
880
+
roles := f.RolesInRepo(user)
881
+
isPushAllowed := roles.IsPushAllowed()
882
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
883
isForkBased := fromFork != "" && sourceBranch != ""
884
isPatchBased := patch != "" && !isBranchBased && !isForkBased
···
1674
return
1675
}
1676
1677
+
roles := f.RolesInRepo(user)
1678
+
if !roles.IsPushAllowed() {
1679
log.Println("unauthorized user")
1680
w.WriteHeader(http.StatusUnauthorized)
1681
return
+30
-2
appview/repo/settings.go
+30
-2
appview/repo/settings.go
···
10
11
"tangled.org/core/api/tangled"
12
"tangled.org/core/appview/db"
13
"tangled.org/core/appview/oauth"
14
"tangled.org/core/appview/pages"
15
xrpcclient "tangled.org/core/appview/xrpcclient"
···
271
f, err := rp.repoResolver.Resolve(r)
272
user := rp.oauth.GetUser(r)
273
274
-
repoCollaborators, err := f.Collaborators(r.Context())
275
if err != nil {
276
l.Error("failed to get collaborators", "err", err)
277
}
···
281
RepoInfo: f.RepoInfo(user),
282
Tabs: settingsTabs,
283
Tab: "access",
284
-
Collaborators: repoCollaborators,
285
})
286
}
287
···
10
11
"tangled.org/core/api/tangled"
12
"tangled.org/core/appview/db"
13
+
"tangled.org/core/appview/models"
14
"tangled.org/core/appview/oauth"
15
"tangled.org/core/appview/pages"
16
xrpcclient "tangled.org/core/appview/xrpcclient"
···
272
f, err := rp.repoResolver.Resolve(r)
273
user := rp.oauth.GetUser(r)
274
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)
303
if err != nil {
304
l.Error("failed to get collaborators", "err", err)
305
}
···
309
RepoInfo: f.RepoInfo(user),
310
Tabs: settingsTabs,
311
Tab: "access",
312
+
Collaborators: collaborators,
313
})
314
}
315
+36
-77
appview/reporesolver/resolver.go
+36
-77
appview/reporesolver/resolver.go
···
1
package reporesolver
2
3
import (
4
-
"context"
5
-
"database/sql"
6
-
"errors"
7
"fmt"
8
"log"
9
"net/http"
···
17
"tangled.org/core/appview/db"
18
"tangled.org/core/appview/models"
19
"tangled.org/core/appview/oauth"
20
-
"tangled.org/core/appview/pages"
21
"tangled.org/core/appview/pages/repoinfo"
22
-
"tangled.org/core/idresolver"
23
"tangled.org/core/rbac"
24
)
25
···
33
}
34
35
type RepoResolver struct {
36
-
config *config.Config
37
-
enforcer *rbac.Enforcer
38
-
idResolver *idresolver.Resolver
39
-
execer db.Execer
40
}
41
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}
44
}
45
46
// NOTE: this... should not even be here. the entire package will be removed in future refactor
···
80
}, nil
81
}
82
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
// this function is a bit weird since it now returns RepoInfo from an entirely different
115
// package. we should refactor this or get rid of RepoInfo entirely.
116
func (f *ResolvedRepo) RepoInfo(user *oauth.User) repoinfo.RepoInfo {
···
120
isStarred = db.GetStarStatus(f.rr.execer, user.Did, repoAt)
121
}
122
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)
145
if err != nil {
146
-
log.Println("failed to get repo by at uri", err)
147
}
148
}
149
150
-
knot := f.Knot
151
152
repoInfo := repoinfo.RepoInfo{
153
OwnerDid: f.OwnerId.DID.String(),
154
OwnerHandle: f.OwnerId.Handle.String(),
155
Name: f.Name,
···
157
Description: f.Description,
158
Website: f.Website,
159
Topics: f.Topics,
160
-
IsStarred: isStarred,
161
-
Knot: knot,
162
Spindle: f.Spindle,
163
-
Roles: f.RolesInRepo(user),
164
-
Stats: models.RepoStats{
165
-
StarCount: starCount,
166
-
IssueCount: issueCount,
167
-
PullCount: pullCount,
168
-
},
169
CurrentDir: f.CurrentDir,
170
Ref: f.Ref,
171
-
}
172
173
-
if sourceRepo != nil {
174
-
repoInfo.Source = sourceRepo
175
}
176
177
return repoInfo
···
1
package reporesolver
2
3
import (
4
"fmt"
5
"log"
6
"net/http"
···
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
···
28
}
29
30
type RepoResolver struct {
31
+
config *config.Config
32
+
enforcer *rbac.Enforcer
33
+
execer db.Execer
34
}
35
36
+
func 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
···
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.
79
func (f *ResolvedRepo) RepoInfo(user *oauth.User) repoinfo.RepoInfo {
···
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,
···
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
+1
-1
appview/state/state.go
+1
-1
appview/state/state.go
+8
rbac/rbac.go
+8
rbac/rbac.go
···
285
return e.E.Enforce(user, domain, repo, "repo:delete")
286
}
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
+
296
func (e *Enforcer) IsPushAllowed(user, domain, repo string) (bool, error) {
297
return e.E.Enforce(user, domain, repo, "repo:push")
298
}