+9
-4
appview/issues/issues.go
+9
-4
appview/issues/issues.go
···
312
// notify about the issue closure
313
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
314
315
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
316
return
317
} else {
318
l.Error("user is not permitted to close issue")
···
362
// notify about the issue reopen
363
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
364
365
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
366
return
367
} else {
368
l.Error("user is not the owner of the repo")
···
466
}
467
rp.notifier.NewIssueComment(r.Context(), &comment, mentions)
468
469
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issue.IssueId, commentId))
470
}
471
472
func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) {
···
988
}
989
}
990
rp.notifier.NewIssue(r.Context(), issue, mentions)
991
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
992
return
993
}
994
}
···
312
// notify about the issue closure
313
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
314
315
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
316
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
317
return
318
} else {
319
l.Error("user is not permitted to close issue")
···
363
// notify about the issue reopen
364
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
365
366
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
367
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
368
return
369
} else {
370
l.Error("user is not the owner of the repo")
···
468
}
469
rp.notifier.NewIssueComment(r.Context(), &comment, mentions)
470
471
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
472
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId))
473
}
474
475
func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) {
···
991
}
992
}
993
rp.notifier.NewIssue(r.Context(), issue, mentions)
994
+
995
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
996
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
997
return
998
}
999
}
+2
-2
appview/middleware/middleware.go
+2
-2
appview/middleware/middleware.go
···
164
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
165
if err != nil || !ok {
166
// we need a logged in user
167
-
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())
168
http.Error(w, "Forbiden", http.StatusUnauthorized)
169
return
170
}
···
327
return
328
}
329
330
-
fullName := f.OwnerHandle() + "/" + f.Name
331
332
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
333
if r.URL.Query().Get("go-get") == "1" {
···
164
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
165
if err != nil || !ok {
166
// we need a logged in user
167
+
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.DidSlashRepo())
168
http.Error(w, "Forbiden", http.StatusUnauthorized)
169
return
170
}
···
327
return
328
}
329
330
+
fullName := reporesolver.GetBaseRepoPath(r, &f.Repo)
331
332
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
333
if r.URL.Query().Get("go-get") == "1" {
+2
-2
appview/pages/repoinfo/repoinfo.go
+2
-2
appview/pages/repoinfo/repoinfo.go
···
21
return path.Join(r.owner(), r.Name)
22
}
23
24
-
func (r RepoInfo) OwnerWithoutAt() string {
25
if r.OwnerHandle != "" {
26
return r.OwnerHandle
27
} else {
···
30
}
31
32
func (r RepoInfo) FullNameWithoutAt() string {
33
-
return path.Join(r.OwnerWithoutAt(), r.Name)
34
}
35
36
func (r RepoInfo) GetTabs() [][]string {
···
21
return path.Join(r.owner(), r.Name)
22
}
23
24
+
func (r RepoInfo) ownerWithoutAt() string {
25
if r.OwnerHandle != "" {
26
return r.OwnerHandle
27
} else {
···
30
}
31
32
func (r RepoInfo) FullNameWithoutAt() string {
33
+
return path.Join(r.ownerWithoutAt(), r.Name)
34
}
35
36
func (r RepoInfo) GetTabs() [][]string {
+24
-16
appview/pulls/pulls.go
+24
-16
appview/pulls/pulls.go
···
268
r.Context(),
269
&xrpcc,
270
&tangled.RepoMergeCheck_Input{
271
-
Did: f.OwnerDid(),
272
Name: f.Name,
273
Branch: pull.TargetBranch,
274
Patch: patch,
···
382
} else {
383
// pulls within the same repo
384
knot = f.Knot
385
-
ownerDid = f.OwnerDid()
386
repoName = f.Name
387
}
388
···
802
}
803
s.notifier.NewPullComment(r.Context(), comment, mentions)
804
805
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId))
806
return
807
}
808
}
···
826
Host: host,
827
}
828
829
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
830
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
831
if err != nil {
832
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
991
Host: host,
992
}
993
994
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
995
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, targetBranch, sourceBranch)
996
if err != nil {
997
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1273
1274
s.notifier.NewPull(r.Context(), pull)
1275
1276
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
1277
}
1278
1279
func (s *Pulls) createStackedPullRequest(
···
1374
return
1375
}
1376
1377
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls", f.OwnerSlashRepo()))
1378
}
1379
1380
func (s *Pulls) ValidatePatch(w http.ResponseWriter, r *http.Request) {
···
1433
Host: host,
1434
}
1435
1436
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1437
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1438
if err != nil {
1439
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1554
Host: targetHost,
1555
}
1556
1557
-
targetRepo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1558
targetXrpcBytes, err := tangled.RepoBranches(r.Context(), targetXrpcc, "", 0, targetRepo)
1559
if err != nil {
1560
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1685
Host: host,
1686
}
1687
1688
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
1689
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch)
1690
if err != nil {
1691
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1922
return
1923
}
1924
1925
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1926
}
1927
1928
func (s *Pulls) resubmitStackedPullHelper(
···
2115
return
2116
}
2117
2118
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
2119
}
2120
2121
func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) {
···
2168
2169
authorName := ident.Handle.String()
2170
mergeInput := &tangled.RepoMerge_Input{
2171
-
Did: f.OwnerDid(),
2172
Name: f.Name,
2173
Branch: pull.TargetBranch,
2174
Patch: patch,
···
2233
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2234
}
2235
2236
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.Name, pull.PullId))
2237
}
2238
2239
func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
···
2305
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2306
}
2307
2308
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
2309
}
2310
2311
func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) {
···
2378
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2379
}
2380
2381
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
2382
}
2383
2384
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {
···
268
r.Context(),
269
&xrpcc,
270
&tangled.RepoMergeCheck_Input{
271
+
Did: f.Did,
272
Name: f.Name,
273
Branch: pull.TargetBranch,
274
Patch: patch,
···
382
} else {
383
// pulls within the same repo
384
knot = f.Knot
385
+
ownerDid = f.Did
386
repoName = f.Name
387
}
388
···
802
}
803
s.notifier.NewPullComment(r.Context(), comment, mentions)
804
805
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
806
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, commentId))
807
return
808
}
809
}
···
827
Host: host,
828
}
829
830
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
831
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
832
if err != nil {
833
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
992
Host: host,
993
}
994
995
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
996
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, targetBranch, sourceBranch)
997
if err != nil {
998
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1274
1275
s.notifier.NewPull(r.Context(), pull)
1276
1277
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
1278
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pullId))
1279
}
1280
1281
func (s *Pulls) createStackedPullRequest(
···
1376
return
1377
}
1378
1379
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
1380
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls", ownerSlashRepo))
1381
}
1382
1383
func (s *Pulls) ValidatePatch(w http.ResponseWriter, r *http.Request) {
···
1436
Host: host,
1437
}
1438
1439
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
1440
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
1441
if err != nil {
1442
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1557
Host: targetHost,
1558
}
1559
1560
+
targetRepo := fmt.Sprintf("%s/%s", f.Did, f.Name)
1561
targetXrpcBytes, err := tangled.RepoBranches(r.Context(), targetXrpcc, "", 0, targetRepo)
1562
if err != nil {
1563
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1688
Host: host,
1689
}
1690
1691
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
1692
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch)
1693
if err != nil {
1694
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
1925
return
1926
}
1927
1928
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
1929
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
1930
}
1931
1932
func (s *Pulls) resubmitStackedPullHelper(
···
2119
return
2120
}
2121
2122
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
2123
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
2124
}
2125
2126
func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) {
···
2173
2174
authorName := ident.Handle.String()
2175
mergeInput := &tangled.RepoMerge_Input{
2176
+
Did: f.Did,
2177
Name: f.Name,
2178
Branch: pull.TargetBranch,
2179
Patch: patch,
···
2238
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2239
}
2240
2241
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
2242
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
2243
}
2244
2245
func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
···
2311
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2312
}
2313
2314
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
2315
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
2316
}
2317
2318
func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) {
···
2385
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
2386
}
2387
2388
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
2389
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
2390
}
2391
2392
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {
+2
-2
appview/repo/archive.go
+2
-2
appview/repo/archive.go
···
31
xrpcc := &indigoxrpc.Client{
32
Host: host,
33
}
34
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
35
-
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, repo)
36
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
37
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
38
rp.pages.Error503(w)
···
31
xrpcc := &indigoxrpc.Client{
32
Host: host,
33
}
34
+
didSlashRepo := f.DidSlashRepo()
35
+
archiveBytes, err := tangled.RepoArchive(r.Context(), xrpcc, "tar.gz", "", ref, didSlashRepo)
36
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
37
l.Error("failed to call XRPC repo.archive", "err", xrpcerr)
38
rp.pages.Error503(w)
+9
-2
appview/repo/artifact.go
+9
-2
appview/repo/artifact.go
···
174
175
artifact := artifacts[0]
176
177
-
ownerPds := f.OwnerId.PDSEndpoint()
178
url, _ := url.Parse(fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob", ownerPds))
179
q := url.Query()
180
q.Set("cid", artifact.BlobCid.String())
···
305
Host: host,
306
}
307
308
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
309
xrpcBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
310
if err != nil {
311
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
174
175
artifact := artifacts[0]
176
177
+
ownerId, err := rp.idResolver.ResolveIdent(r.Context(), f.Did)
178
+
if err != nil {
179
+
log.Println("failed to resolve repo owner did", f.Did, err)
180
+
http.Error(w, "repository owner not found", http.StatusNotFound)
181
+
return
182
+
}
183
+
184
+
ownerPds := ownerId.PDSEndpoint()
185
url, _ := url.Parse(fmt.Sprintf("%s/xrpc/com.atproto.sync.getBlob", ownerPds))
186
q := url.Query()
187
q.Set("cid", artifact.BlobCid.String())
···
312
Host: host,
313
}
314
315
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
316
xrpcBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
317
if err != nil {
318
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
+6
-4
appview/repo/blob.go
+6
-4
appview/repo/blob.go
···
54
xrpcc := &indigoxrpc.Client{
55
Host: host,
56
}
57
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
58
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
59
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
60
l.Error("failed to call XRPC repo.blob", "err", xrpcerr)
···
62
return
63
}
64
65
// Use XRPC response directly instead of converting to internal types
66
var breadcrumbs [][]string
67
-
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})
68
if filePath != "" {
69
for idx, elem := range strings.Split(filePath, "/") {
70
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
105
if !rp.config.Core.Dev {
106
scheme = "https"
107
}
108
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
109
baseURL := &url.URL{
110
Scheme: scheme,
111
Host: f.Knot,
···
256
scheme = "https"
257
}
258
259
-
repoName := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
260
baseURL := &url.URL{
261
Scheme: scheme,
262
Host: f.Knot,
···
54
xrpcc := &indigoxrpc.Client{
55
Host: host,
56
}
57
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Repo.Name)
58
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
59
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
60
l.Error("failed to call XRPC repo.blob", "err", xrpcerr)
···
62
return
63
}
64
65
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
66
+
67
// Use XRPC response directly instead of converting to internal types
68
var breadcrumbs [][]string
69
+
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})
70
if filePath != "" {
71
for idx, elem := range strings.Split(filePath, "/") {
72
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
107
if !rp.config.Core.Dev {
108
scheme = "https"
109
}
110
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Repo.Name)
111
baseURL := &url.URL{
112
Scheme: scheme,
113
Host: f.Knot,
···
258
scheme = "https"
259
}
260
261
+
repoName := fmt.Sprintf("%s/%s", f.Did, f.Name)
262
baseURL := &url.URL{
263
Scheme: scheme,
264
Host: f.Knot,
+1
-1
appview/repo/branches.go
+1
-1
appview/repo/branches.go
···
29
xrpcc := &indigoxrpc.Client{
30
Host: host,
31
}
32
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
33
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
34
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
35
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
29
xrpcc := &indigoxrpc.Client{
30
Host: host,
31
}
32
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
33
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
34
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
35
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
+2
-2
appview/repo/compare.go
+2
-2
appview/repo/compare.go
···
36
Host: host,
37
}
38
39
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
40
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
41
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
42
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
151
Host: host,
152
}
153
154
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
155
156
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
157
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
36
Host: host,
37
}
38
39
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
40
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
41
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
42
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
151
Host: host,
152
}
153
154
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
155
156
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
157
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
+23
-17
appview/repo/feed.go
+23
-17
appview/repo/feed.go
···
11
"tangled.org/core/appview/db"
12
"tangled.org/core/appview/models"
13
"tangled.org/core/appview/pagination"
14
-
"tangled.org/core/appview/reporesolver"
15
16
"github.com/bluesky-social/indigo/atproto/syntax"
17
"github.com/gorilla/feeds"
18
)
19
20
-
func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) {
21
const feedLimitPerType = 100
22
23
-
pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt()))
24
if err != nil {
25
return nil, err
26
}
···
28
issues, err := db.GetIssuesPaginated(
29
rp.db,
30
pagination.Page{Limit: feedLimitPerType},
31
-
db.FilterEq("repo_at", f.RepoAt()),
32
)
33
if err != nil {
34
return nil, err
35
}
36
37
feed := &feeds.Feed{
38
-
Title: fmt.Sprintf("activity feed for %s", f.OwnerSlashRepo()),
39
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"},
40
Items: make([]*feeds.Item, 0),
41
Updated: time.UnixMilli(0),
42
}
43
44
for _, pull := range pulls {
45
-
items, err := rp.createPullItems(ctx, pull, f)
46
if err != nil {
47
return nil, err
48
}
···
50
}
51
52
for _, issue := range issues {
53
-
item, err := rp.createIssueItem(ctx, issue, f)
54
if err != nil {
55
return nil, err
56
}
···
71
return feed, nil
72
}
73
74
-
func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) {
75
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
76
if err != nil {
77
return nil, err
···
80
var items []*feeds.Item
81
82
state := rp.getPullState(pull)
83
-
description := rp.buildPullDescription(owner.Handle, state, pull, f.OwnerSlashRepo())
84
85
mainItem := &feeds.Item{
86
Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title),
87
Description: description,
88
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)},
89
Created: pull.Created,
90
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
91
}
···
98
99
roundItem := &feeds.Item{
100
Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber),
101
-
Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in %s", owner.Handle, round.RoundNumber, pull.PullId, f.OwnerSlashRepo()),
102
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId, round.RoundNumber)},
103
Created: round.Created,
104
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
105
}
···
109
return items, nil
110
}
111
112
-
func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) {
113
owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did)
114
if err != nil {
115
return nil, err
···
122
123
return &feeds.Item{
124
Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title),
125
-
Description: fmt.Sprintf("@%s %s issue #%d in %s", owner.Handle, state, issue.IssueId, f.OwnerSlashRepo()),
126
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)},
127
Created: issue.Created,
128
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
129
}, nil
···
152
log.Println("failed to fully resolve repo:", err)
153
return
154
}
155
156
-
feed, err := rp.getRepoFeed(r.Context(), f)
157
if err != nil {
158
log.Println("failed to get repo feed:", err)
159
rp.pages.Error500(w)
···
11
"tangled.org/core/appview/db"
12
"tangled.org/core/appview/models"
13
"tangled.org/core/appview/pagination"
14
15
+
"github.com/bluesky-social/indigo/atproto/identity"
16
"github.com/bluesky-social/indigo/atproto/syntax"
17
"github.com/gorilla/feeds"
18
)
19
20
+
func (rp *Repo) getRepoFeed(ctx context.Context, repo *models.Repo, ownerSlashRepo string) (*feeds.Feed, error) {
21
const feedLimitPerType = 100
22
23
+
pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", repo.RepoAt()))
24
if err != nil {
25
return nil, err
26
}
···
28
issues, err := db.GetIssuesPaginated(
29
rp.db,
30
pagination.Page{Limit: feedLimitPerType},
31
+
db.FilterEq("repo_at", repo.RepoAt()),
32
)
33
if err != nil {
34
return nil, err
35
}
36
37
feed := &feeds.Feed{
38
+
Title: fmt.Sprintf("activity feed for @%s", ownerSlashRepo),
39
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, ownerSlashRepo), Type: "text/html", Rel: "alternate"},
40
Items: make([]*feeds.Item, 0),
41
Updated: time.UnixMilli(0),
42
}
43
44
for _, pull := range pulls {
45
+
items, err := rp.createPullItems(ctx, pull, repo, ownerSlashRepo)
46
if err != nil {
47
return nil, err
48
}
···
50
}
51
52
for _, issue := range issues {
53
+
item, err := rp.createIssueItem(ctx, issue, repo, ownerSlashRepo)
54
if err != nil {
55
return nil, err
56
}
···
71
return feed, nil
72
}
73
74
+
func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, repo *models.Repo, ownerSlashRepo string) ([]*feeds.Item, error) {
75
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
76
if err != nil {
77
return nil, err
···
80
var items []*feeds.Item
81
82
state := rp.getPullState(pull)
83
+
description := rp.buildPullDescription(owner.Handle, state, pull, ownerSlashRepo)
84
85
mainItem := &feeds.Item{
86
Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title),
87
Description: description,
88
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId)},
89
Created: pull.Created,
90
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
91
}
···
98
99
roundItem := &feeds.Item{
100
Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber),
101
+
Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in @%s", owner.Handle, round.RoundNumber, pull.PullId, ownerSlashRepo),
102
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId, round.RoundNumber)},
103
Created: round.Created,
104
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
105
}
···
109
return items, nil
110
}
111
112
+
func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, repo *models.Repo, ownerSlashRepo string) (*feeds.Item, error) {
113
owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did)
114
if err != nil {
115
return nil, err
···
122
123
return &feeds.Item{
124
Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title),
125
+
Description: fmt.Sprintf("@%s %s issue #%d in @%s", owner.Handle, state, issue.IssueId, ownerSlashRepo),
126
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, ownerSlashRepo, issue.IssueId)},
127
Created: issue.Created,
128
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
129
}, nil
···
152
log.Println("failed to fully resolve repo:", err)
153
return
154
}
155
+
repoOwnerId, ok := r.Context().Value("resolvedId").(identity.Identity)
156
+
if !ok || repoOwnerId.Handle.IsInvalidHandle() {
157
+
log.Println("failed to get resolved repo owner id")
158
+
return
159
+
}
160
+
ownerSlashRepo := repoOwnerId.Handle.String() + "/" + f.Name
161
162
+
feed, err := rp.getRepoFeed(r.Context(), &f.Repo, ownerSlashRepo)
163
if err != nil {
164
log.Println("failed to get repo feed:", err)
165
rp.pages.Error500(w)
+2
-2
appview/repo/index.go
+2
-2
appview/repo/index.go
···
179
180
if err != nil || langs == nil {
181
// non-fatal, fetch langs from ks via XRPC
182
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
183
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
184
if err != nil {
185
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
256
257
// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
258
func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {
259
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
260
261
// first get branches to determine the ref if not specified
262
branchesBytes, err := tangled.RepoBranches(ctx, xrpcc, "", 0, repo)
···
179
180
if err != nil || langs == nil {
181
// non-fatal, fetch langs from ks via XRPC
182
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
183
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
184
if err != nil {
185
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
256
257
// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
258
func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {
259
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
260
261
// first get branches to determine the ref if not specified
262
branchesBytes, err := tangled.RepoBranches(ctx, xrpcc, "", 0, repo)
+2
-2
appview/repo/log.go
+2
-2
appview/repo/log.go
···
57
cursor = strconv.Itoa(offset)
58
}
59
60
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
61
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
62
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
63
l.Error("failed to call XRPC repo.log", "err", xrpcerr)
···
174
Host: host,
175
}
176
177
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
178
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
179
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
180
l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
···
57
cursor = strconv.Itoa(offset)
58
}
59
60
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
61
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
62
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
63
l.Error("failed to call XRPC repo.log", "err", xrpcerr)
···
174
Host: host,
175
}
176
177
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
178
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
179
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
180
l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
+5
-5
appview/repo/repo.go
+5
-5
appview/repo/repo.go
···
864
r.Context(),
865
client,
866
&tangled.RepoDelete_Input{
867
-
Did: f.OwnerDid(),
868
Name: f.Name,
869
Rkey: f.Rkey,
870
},
···
902
l.Info("removed collaborators")
903
904
// remove repo RBAC
905
-
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
906
if err != nil {
907
rp.pages.Notice(w, noticeId, "Failed to update RBAC rules")
908
return
909
}
910
911
// remove repo from db
912
-
err = db.RemoveRepo(tx, f.OwnerDid(), f.Name)
913
if err != nil {
914
rp.pages.Notice(w, noticeId, "Failed to update appview")
915
return
···
930
return
931
}
932
933
-
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
934
}
935
936
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
···
1058
uri = "http"
1059
}
1060
1061
-
forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.Repo.Name)
1062
l = l.With("cloneUrl", forkSourceUrl)
1063
1064
sourceAt := f.RepoAt().String()
···
864
r.Context(),
865
client,
866
&tangled.RepoDelete_Input{
867
+
Did: f.Did,
868
Name: f.Name,
869
Rkey: f.Rkey,
870
},
···
902
l.Info("removed collaborators")
903
904
// remove repo RBAC
905
+
err = rp.enforcer.RemoveRepo(f.Did, f.Knot, f.DidSlashRepo())
906
if err != nil {
907
rp.pages.Notice(w, noticeId, "Failed to update RBAC rules")
908
return
909
}
910
911
// remove repo from db
912
+
err = db.RemoveRepo(tx, f.Did, f.Name)
913
if err != nil {
914
rp.pages.Notice(w, noticeId, "Failed to update appview")
915
return
···
930
return
931
}
932
933
+
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.Did))
934
}
935
936
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
···
1058
uri = "http"
1059
}
1060
1061
+
forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.Did, f.Repo.Name)
1062
l = l.With("cloneUrl", forkSourceUrl)
1063
1064
sourceAt := f.RepoAt().String()
+2
-2
appview/repo/settings.go
+2
-2
appview/repo/settings.go
···
194
Host: host,
195
}
196
197
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
198
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
199
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
200
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
292
user := rp.oauth.GetUser(r)
293
294
// all spindles that the repo owner is a member of
295
-
spindles, err := rp.enforcer.GetSpindlesForUser(f.OwnerDid())
296
if err != nil {
297
l.Error("failed to fetch spindles", "err", err)
298
return
···
194
Host: host,
195
}
196
197
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
198
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
199
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
200
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
292
user := rp.oauth.GetUser(r)
293
294
// all spindles that the repo owner is a member of
295
+
spindles, err := rp.enforcer.GetSpindlesForUser(f.Did)
296
if err != nil {
297
l.Error("failed to fetch spindles", "err", err)
298
return
+5
-3
appview/repo/tree.go
+5
-3
appview/repo/tree.go
···
9
10
"tangled.org/core/api/tangled"
11
"tangled.org/core/appview/pages"
12
xrpcclient "tangled.org/core/appview/xrpcclient"
13
"tangled.org/core/types"
14
···
39
xrpcc := &indigoxrpc.Client{
40
Host: host,
41
}
42
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
43
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
44
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
45
l.Error("failed to call XRPC repo.tree", "err", xrpcerr)
···
79
result.ReadmeFileName = xrpcResp.Readme.Filename
80
result.Readme = xrpcResp.Readme.Contents
81
}
82
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
83
// so we can safely redirect to the "parent" (which is the same file).
84
if len(result.Files) == 0 && result.Parent == treePath {
85
-
redirectTo := fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), url.PathEscape(ref), result.Parent)
86
http.Redirect(w, r, redirectTo, http.StatusFound)
87
return
88
}
89
user := rp.oauth.GetUser(r)
90
var breadcrumbs [][]string
91
-
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})
92
if treePath != "" {
93
for idx, elem := range strings.Split(treePath, "/") {
94
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
9
10
"tangled.org/core/api/tangled"
11
"tangled.org/core/appview/pages"
12
+
"tangled.org/core/appview/reporesolver"
13
xrpcclient "tangled.org/core/appview/xrpcclient"
14
"tangled.org/core/types"
15
···
40
xrpcc := &indigoxrpc.Client{
41
Host: host,
42
}
43
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
44
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
45
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
46
l.Error("failed to call XRPC repo.tree", "err", xrpcerr)
···
80
result.ReadmeFileName = xrpcResp.Readme.Filename
81
result.Readme = xrpcResp.Readme.Contents
82
}
83
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
84
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
85
// so we can safely redirect to the "parent" (which is the same file).
86
if len(result.Files) == 0 && result.Parent == treePath {
87
+
redirectTo := fmt.Sprintf("/%s/blob/%s/%s", ownerSlashRepo, url.PathEscape(ref), result.Parent)
88
http.Redirect(w, r, redirectTo, http.StatusFound)
89
return
90
}
91
user := rp.oauth.GetUser(r)
92
var breadcrumbs [][]string
93
+
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})
94
if treePath != "" {
95
for idx, elem := range strings.Split(treePath, "/") {
96
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
+15
-25
appview/reporesolver/resolver.go
+15
-25
appview/reporesolver/resolver.go
···
12
"strings"
13
14
"github.com/bluesky-social/indigo/atproto/identity"
15
-
securejoin "github.com/cyphar/filepath-securejoin"
16
"github.com/go-chi/chi/v5"
17
"tangled.org/core/appview/config"
18
"tangled.org/core/appview/db"
···
44
return &RepoResolver{config: config, enforcer: enforcer, idResolver: resolver, execer: execer}
45
}
46
47
func (rr *RepoResolver) Resolve(r *http.Request) (*ResolvedRepo, error) {
48
repo, ok := r.Context().Value("repo").(*models.Repo)
49
if !ok {
···
67
68
rr: rr,
69
}, nil
70
-
}
71
-
72
-
func (f *ResolvedRepo) OwnerDid() string {
73
-
return f.OwnerId.DID.String()
74
-
}
75
-
76
-
func (f *ResolvedRepo) OwnerHandle() string {
77
-
return f.OwnerId.Handle.String()
78
-
}
79
-
80
-
func (f *ResolvedRepo) OwnerSlashRepo() string {
81
-
handle := f.OwnerId.Handle
82
-
83
-
var p string
84
-
if handle != "" && !handle.IsInvalidHandle() {
85
-
p, _ = securejoin.SecureJoin(fmt.Sprintf("@%s", handle), f.Name)
86
-
} else {
87
-
p, _ = securejoin.SecureJoin(f.OwnerDid(), f.Name)
88
-
}
89
-
90
-
return p
91
}
92
93
func (f *ResolvedRepo) Collaborators(ctx context.Context) ([]pages.Collaborator, error) {
···
168
knot := f.Knot
169
170
repoInfo := repoinfo.RepoInfo{
171
-
OwnerDid: f.OwnerDid(),
172
-
OwnerHandle: f.OwnerHandle(),
173
Name: f.Name,
174
-
Rkey: f.Repo.Rkey,
175
RepoAt: repoAt,
176
Description: f.Description,
177
Website: f.Website,
···
12
"strings"
13
14
"github.com/bluesky-social/indigo/atproto/identity"
15
"github.com/go-chi/chi/v5"
16
"tangled.org/core/appview/config"
17
"tangled.org/core/appview/db"
···
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
47
+
func GetBaseRepoPath(r *http.Request, repo *models.Repo) string {
48
+
var (
49
+
user = chi.URLParam(r, "user")
50
+
name = chi.URLParam(r, "repo")
51
+
)
52
+
if user == "" || name == "" {
53
+
return repo.DidSlashRepo()
54
+
}
55
+
return path.Join(user, name)
56
+
}
57
+
58
func (rr *RepoResolver) Resolve(r *http.Request) (*ResolvedRepo, error) {
59
repo, ok := r.Context().Value("repo").(*models.Repo)
60
if !ok {
···
78
79
rr: rr,
80
}, nil
81
}
82
83
func (f *ResolvedRepo) Collaborators(ctx context.Context) ([]pages.Collaborator, error) {
···
158
knot := f.Knot
159
160
repoInfo := repoinfo.RepoInfo{
161
+
OwnerDid: f.OwnerId.DID.String(),
162
+
OwnerHandle: f.OwnerId.Handle.String(),
163
Name: f.Name,
164
+
Rkey: f.Rkey,
165
RepoAt: repoAt,
166
Description: f.Description,
167
Website: f.Website,