appview: use less OwnerSlashRepo() in handlers #802

merged
opened by boltless.me targeting master from sl/yurolxtlpsmz
Changed files
+102 -89
appview
+9 -4
appview/issues/issues.go
···
// notify about the issue closure
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
return
} else {
l.Error("user is not permitted to close issue")
···
// notify about the issue reopen
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
return
} else {
l.Error("user is not the owner of the repo")
···
}
rp.notifier.NewIssueComment(r.Context(), &comment, mentions)
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issue.IssueId, commentId))
}
func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) {
···
}
}
rp.notifier.NewIssue(r.Context(), issue, mentions)
-
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId))
return
}
}
···
// notify about the issue closure
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
return
} else {
l.Error("user is not permitted to close issue")
···
// notify about the issue reopen
rp.notifier.NewIssueState(r.Context(), syntax.DID(user.Did), issue)
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
return
} else {
l.Error("user is not the owner of the repo")
···
}
rp.notifier.NewIssueComment(r.Context(), &comment, mentions)
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", ownerSlashRepo, issue.IssueId, commentId))
}
func (rp *Issues) IssueComment(w http.ResponseWriter, r *http.Request) {
···
}
}
rp.notifier.NewIssue(r.Context(), issue, mentions)
+
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", ownerSlashRepo, issue.IssueId))
return
}
}
+2 -2
appview/middleware/middleware.go
···
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
if err != nil || !ok {
// we need a logged in user
-
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.OwnerSlashRepo())
http.Error(w, "Forbiden", http.StatusUnauthorized)
return
}
···
return
}
-
fullName := f.OwnerHandle() + "/" + f.Name
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
if r.URL.Query().Get("go-get") == "1" {
···
ok, err := mw.enforcer.E.Enforce(actor.Did, f.Knot, f.DidSlashRepo(), requiredPerm)
if err != nil || !ok {
// we need a logged in user
+
log.Printf("%s does not have perms of a %s in repo %s", actor.Did, requiredPerm, f.DidSlashRepo())
http.Error(w, "Forbiden", http.StatusUnauthorized)
return
}
···
return
}
+
fullName := reporesolver.GetBaseRepoPath(r, &f.Repo)
if r.Header.Get("User-Agent") == "Go-http-client/1.1" {
if r.URL.Query().Get("go-get") == "1" {
+2 -2
appview/pages/repoinfo/repoinfo.go
···
return path.Join(r.owner(), r.Name)
}
-
func (r RepoInfo) OwnerWithoutAt() string {
if r.OwnerHandle != "" {
return r.OwnerHandle
} else {
···
}
func (r RepoInfo) FullNameWithoutAt() string {
-
return path.Join(r.OwnerWithoutAt(), r.Name)
}
func (r RepoInfo) GetTabs() [][]string {
···
return path.Join(r.owner(), r.Name)
}
+
func (r RepoInfo) ownerWithoutAt() string {
if r.OwnerHandle != "" {
return r.OwnerHandle
} else {
···
}
func (r RepoInfo) FullNameWithoutAt() string {
+
return path.Join(r.ownerWithoutAt(), r.Name)
}
func (r RepoInfo) GetTabs() [][]string {
+24 -16
appview/pulls/pulls.go
···
r.Context(),
&xrpcc,
&tangled.RepoMergeCheck_Input{
-
Did: f.OwnerDid(),
Name: f.Name,
Branch: pull.TargetBranch,
Patch: patch,
···
} else {
// pulls within the same repo
knot = f.Knot
-
ownerDid = f.OwnerDid()
repoName = f.Name
}
···
}
s.notifier.NewPullComment(r.Context(), comment, mentions)
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId))
return
}
}
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, targetBranch, sourceBranch)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
s.notifier.NewPull(r.Context(), pull)
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
}
func (s *Pulls) createStackedPullRequest(
···
return
}
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls", f.OwnerSlashRepo()))
}
func (s *Pulls) ValidatePatch(w http.ResponseWriter, r *http.Request) {
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: targetHost,
}
-
targetRepo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
targetXrpcBytes, err := tangled.RepoBranches(r.Context(), targetXrpcc, "", 0, targetRepo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
return
}
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
func (s *Pulls) resubmitStackedPullHelper(
···
return
}
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) {
···
authorName := ident.Handle.String()
mergeInput := &tangled.RepoMerge_Input{
-
Did: f.OwnerDid(),
Name: f.Name,
Branch: pull.TargetBranch,
Patch: patch,
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.Name, pull.PullId))
}
func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) {
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
}
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {
···
r.Context(),
&xrpcc,
&tangled.RepoMergeCheck_Input{
+
Did: f.Did,
Name: f.Name,
Branch: pull.TargetBranch,
Patch: patch,
···
} else {
// pulls within the same repo
knot = f.Knot
+
ownerDid = f.Did
repoName = f.Name
}
···
}
s.notifier.NewPullComment(r.Context(), comment, mentions)
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", ownerSlashRepo, pull.PullId, commentId))
return
}
}
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, targetBranch, sourceBranch)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
s.notifier.NewPull(r.Context(), pull)
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pullId))
}
func (s *Pulls) createStackedPullRequest(
···
return
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls", ownerSlashRepo))
}
func (s *Pulls) ValidatePatch(w http.ResponseWriter, r *http.Request) {
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: targetHost,
}
+
targetRepo := fmt.Sprintf("%s/%s", f.Did, f.Name)
targetXrpcBytes, err := tangled.RepoBranches(r.Context(), targetXrpcc, "", 0, targetRepo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
return
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
}
func (s *Pulls) resubmitStackedPullHelper(
···
return
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
}
func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) {
···
authorName := ident.Handle.String()
mergeInput := &tangled.RepoMerge_Input{
+
Did: f.Did,
Name: f.Name,
Branch: pull.TargetBranch,
Patch: patch,
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
}
func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
}
func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) {
···
s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p)
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
}
func newStack(f *reporesolver.ResolvedRepo, user *oauth.User, targetBranch, patch string, pullSource *models.PullSource, stackId string) (models.Stack, error) {
+1 -1
appview/repo/artifact.go
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoTags(ctx, xrpcc, "", 0, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
+6 -4
appview/repo/blob.go
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.blob", "err", xrpcerr)
···
return
}
// Use XRPC response directly instead of converting to internal types
var breadcrumbs [][]string
-
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})
if filePath != "" {
for idx, elem := range strings.Split(filePath, "/") {
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
if !rp.config.Core.Dev {
scheme = "https"
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Repo.Name)
baseURL := &url.URL{
Scheme: scheme,
Host: f.Knot,
···
scheme = "https"
}
-
repoName := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
baseURL := &url.URL{
Scheme: scheme,
Host: f.Knot,
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Repo.Name)
resp, err := tangled.RepoBlob(r.Context(), xrpcc, filePath, false, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.blob", "err", xrpcerr)
···
return
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
+
// Use XRPC response directly instead of converting to internal types
var breadcrumbs [][]string
+
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})
if filePath != "" {
for idx, elem := range strings.Split(filePath, "/") {
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
if !rp.config.Core.Dev {
scheme = "https"
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Repo.Name)
baseURL := &url.URL{
Scheme: scheme,
Host: f.Knot,
···
scheme = "https"
}
+
repoName := fmt.Sprintf("%s/%s", f.Did, f.Name)
baseURL := &url.URL{
Scheme: scheme,
Host: f.Knot,
+1 -1
appview/repo/branches.go
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
+2 -2
appview/repo/compare.go
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
+23 -17
appview/repo/feed.go
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/appview/pagination"
-
"tangled.org/core/appview/reporesolver"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/gorilla/feeds"
)
-
func (rp *Repo) getRepoFeed(ctx context.Context, f *reporesolver.ResolvedRepo) (*feeds.Feed, error) {
const feedLimitPerType = 100
-
pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", f.RepoAt()))
if err != nil {
return nil, err
}
···
issues, err := db.GetIssuesPaginated(
rp.db,
pagination.Page{Limit: feedLimitPerType},
-
db.FilterEq("repo_at", f.RepoAt()),
)
if err != nil {
return nil, err
}
feed := &feeds.Feed{
-
Title: fmt.Sprintf("activity feed for %s", f.OwnerSlashRepo()),
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, f.OwnerSlashRepo()), Type: "text/html", Rel: "alternate"},
Items: make([]*feeds.Item, 0),
Updated: time.UnixMilli(0),
}
for _, pull := range pulls {
-
items, err := rp.createPullItems(ctx, pull, f)
if err != nil {
return nil, err
}
···
}
for _, issue := range issues {
-
item, err := rp.createIssueItem(ctx, issue, f)
if err != nil {
return nil, err
}
···
return feed, nil
}
-
func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, f *reporesolver.ResolvedRepo) ([]*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
if err != nil {
return nil, err
···
var items []*feeds.Item
state := rp.getPullState(pull)
-
description := rp.buildPullDescription(owner.Handle, state, pull, f.OwnerSlashRepo())
mainItem := &feeds.Item{
Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title),
Description: description,
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId)},
Created: pull.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}
···
roundItem := &feeds.Item{
Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber),
-
Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in %s", owner.Handle, round.RoundNumber, pull.PullId, f.OwnerSlashRepo()),
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), pull.PullId, round.RoundNumber)},
Created: round.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}
···
return items, nil
}
-
func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, f *reporesolver.ResolvedRepo) (*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did)
if err != nil {
return nil, err
···
return &feeds.Item{
Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title),
-
Description: fmt.Sprintf("@%s %s issue #%d in %s", owner.Handle, state, issue.IssueId, f.OwnerSlashRepo()),
-
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, f.OwnerSlashRepo(), issue.IssueId)},
Created: issue.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}, nil
···
log.Println("failed to fully resolve repo:", err)
return
}
-
feed, err := rp.getRepoFeed(r.Context(), f)
if err != nil {
log.Println("failed to get repo feed:", err)
rp.pages.Error500(w)
···
"tangled.org/core/appview/db"
"tangled.org/core/appview/models"
"tangled.org/core/appview/pagination"
+
"github.com/bluesky-social/indigo/atproto/identity"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/gorilla/feeds"
)
+
func (rp *Repo) getRepoFeed(ctx context.Context, repo *models.Repo, ownerSlashRepo string) (*feeds.Feed, error) {
const feedLimitPerType = 100
+
pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", repo.RepoAt()))
if err != nil {
return nil, err
}
···
issues, err := db.GetIssuesPaginated(
rp.db,
pagination.Page{Limit: feedLimitPerType},
+
db.FilterEq("repo_at", repo.RepoAt()),
)
if err != nil {
return nil, err
}
feed := &feeds.Feed{
+
Title: fmt.Sprintf("activity feed for @%s", ownerSlashRepo),
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s", rp.config.Core.AppviewHost, ownerSlashRepo), Type: "text/html", Rel: "alternate"},
Items: make([]*feeds.Item, 0),
Updated: time.UnixMilli(0),
}
for _, pull := range pulls {
+
items, err := rp.createPullItems(ctx, pull, repo, ownerSlashRepo)
if err != nil {
return nil, err
}
···
}
for _, issue := range issues {
+
item, err := rp.createIssueItem(ctx, issue, repo, ownerSlashRepo)
if err != nil {
return nil, err
}
···
return feed, nil
}
+
func (rp *Repo) createPullItems(ctx context.Context, pull *models.Pull, repo *models.Repo, ownerSlashRepo string) ([]*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, pull.OwnerDid)
if err != nil {
return nil, err
···
var items []*feeds.Item
state := rp.getPullState(pull)
+
description := rp.buildPullDescription(owner.Handle, state, pull, ownerSlashRepo)
mainItem := &feeds.Item{
Title: fmt.Sprintf("[PR #%d] %s", pull.PullId, pull.Title),
Description: description,
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId)},
Created: pull.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}
···
roundItem := &feeds.Item{
Title: fmt.Sprintf("[PR #%d] %s (round #%d)", pull.PullId, pull.Title, round.RoundNumber),
+
Description: fmt.Sprintf("@%s submitted changes (at round #%d) on PR #%d in @%s", owner.Handle, round.RoundNumber, pull.PullId, ownerSlashRepo),
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/pulls/%d/round/%d/", rp.config.Core.AppviewHost, ownerSlashRepo, pull.PullId, round.RoundNumber)},
Created: round.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}
···
return items, nil
}
+
func (rp *Repo) createIssueItem(ctx context.Context, issue models.Issue, repo *models.Repo, ownerSlashRepo string) (*feeds.Item, error) {
owner, err := rp.idResolver.ResolveIdent(ctx, issue.Did)
if err != nil {
return nil, err
···
return &feeds.Item{
Title: fmt.Sprintf("[Issue #%d] %s", issue.IssueId, issue.Title),
+
Description: fmt.Sprintf("@%s %s issue #%d in @%s", owner.Handle, state, issue.IssueId, ownerSlashRepo),
+
Link: &feeds.Link{Href: fmt.Sprintf("%s/%s/issues/%d", rp.config.Core.AppviewHost, ownerSlashRepo, issue.IssueId)},
Created: issue.Created,
Author: &feeds.Author{Name: fmt.Sprintf("@%s", owner.Handle)},
}, nil
···
log.Println("failed to fully resolve repo:", err)
return
}
+
repoOwnerId, ok := r.Context().Value("resolvedId").(identity.Identity)
+
if !ok || repoOwnerId.Handle.IsInvalidHandle() {
+
log.Println("failed to get resolved repo owner id")
+
return
+
}
+
ownerSlashRepo := repoOwnerId.Handle.String() + "/" + f.Name
+
feed, err := rp.getRepoFeed(r.Context(), &f.Repo, ownerSlashRepo)
if err != nil {
log.Println("failed to get repo feed:", err)
rp.pages.Error500(w)
+2 -2
appview/repo/index.go
···
if err != nil || langs == nil {
// non-fatal, fetch langs from ks via XRPC
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
// first get branches to determine the ref if not specified
branchesBytes, err := tangled.RepoBranches(ctx, xrpcc, "", 0, repo)
···
if err != nil || langs == nil {
// non-fatal, fetch langs from ks via XRPC
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
ls, err := tangled.RepoLanguages(ctx, xrpcc, currentRef, repo)
if err != nil {
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
···
// buildIndexResponse creates a RepoIndexResponse by combining multiple xrpc calls in parallel
func (rp *Repo) buildIndexResponse(ctx context.Context, xrpcc *indigoxrpc.Client, f *reporesolver.ResolvedRepo, ref string) (*types.RepoIndexResponse, error) {
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
// first get branches to determine the ref if not specified
branchesBytes, err := tangled.RepoBranches(ctx, xrpcc, "", 0, repo)
+2 -2
appview/repo/log.go
···
cursor = strconv.Itoa(offset)
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.log", "err", xrpcerr)
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
···
cursor = strconv.Itoa(offset)
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoLog(r.Context(), xrpcc, cursor, limit, "", ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.log", "err", xrpcerr)
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoDiff(r.Context(), xrpcc, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.diff", "err", xrpcerr)
+5 -5
appview/repo/repo.go
···
r.Context(),
client,
&tangled.RepoDelete_Input{
-
Did: f.OwnerDid(),
Name: f.Name,
Rkey: f.Rkey,
},
···
l.Info("removed collaborators")
// remove repo RBAC
-
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
if err != nil {
rp.pages.Notice(w, noticeId, "Failed to update RBAC rules")
return
}
// remove repo from db
-
err = db.RemoveRepo(tx, f.OwnerDid(), f.Name)
if err != nil {
rp.pages.Notice(w, noticeId, "Failed to update appview")
return
···
return
}
-
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
}
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
···
uri = "http"
}
-
forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.Repo.Name)
l = l.With("cloneUrl", forkSourceUrl)
sourceAt := f.RepoAt().String()
···
r.Context(),
client,
&tangled.RepoDelete_Input{
+
Did: f.Did,
Name: f.Name,
Rkey: f.Rkey,
},
···
l.Info("removed collaborators")
// remove repo RBAC
+
err = rp.enforcer.RemoveRepo(f.Did, f.Knot, f.DidSlashRepo())
if err != nil {
rp.pages.Notice(w, noticeId, "Failed to update RBAC rules")
return
}
// remove repo from db
+
err = db.RemoveRepo(tx, f.Did, f.Name)
if err != nil {
rp.pages.Notice(w, noticeId, "Failed to update appview")
return
···
return
}
+
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.Did))
}
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
···
uri = "http"
}
+
forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.Did, f.Repo.Name)
l = l.With("cloneUrl", forkSourceUrl)
sourceAt := f.RepoAt().String()
+2 -2
appview/repo/settings.go
···
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
user := rp.oauth.GetUser(r)
// all spindles that the repo owner is a member of
-
spindles, err := rp.enforcer.GetSpindlesForUser(f.OwnerDid())
if err != nil {
l.Error("failed to fetch spindles", "err", err)
return
···
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.branches", "err", xrpcerr)
···
user := rp.oauth.GetUser(r)
// all spindles that the repo owner is a member of
+
spindles, err := rp.enforcer.GetSpindlesForUser(f.Did)
if err != nil {
l.Error("failed to fetch spindles", "err", err)
return
+1 -1
appview/repo/tags.go
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.tags", "err", xrpcerr)
+5 -3
appview/repo/tree.go
···
"tangled.org/core/api/tangled"
"tangled.org/core/appview/pages"
xrpcclient "tangled.org/core/appview/xrpcclient"
"tangled.org/core/types"
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
-
repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name)
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.tree", "err", xrpcerr)
···
result.ReadmeFileName = xrpcResp.Readme.Filename
result.Readme = xrpcResp.Readme.Contents
}
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
// so we can safely redirect to the "parent" (which is the same file).
if len(result.Files) == 0 && result.Parent == treePath {
-
redirectTo := fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), url.PathEscape(ref), result.Parent)
http.Redirect(w, r, redirectTo, http.StatusFound)
return
}
user := rp.oauth.GetUser(r)
var breadcrumbs [][]string
-
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), url.PathEscape(ref))})
if treePath != "" {
for idx, elem := range strings.Split(treePath, "/") {
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
···
"tangled.org/core/api/tangled"
"tangled.org/core/appview/pages"
+
"tangled.org/core/appview/reporesolver"
xrpcclient "tangled.org/core/appview/xrpcclient"
"tangled.org/core/types"
···
xrpcc := &indigoxrpc.Client{
Host: host,
}
+
repo := fmt.Sprintf("%s/%s", f.Did, f.Name)
xrpcResp, err := tangled.RepoTree(r.Context(), xrpcc, treePath, ref, repo)
if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil {
l.Error("failed to call XRPC repo.tree", "err", xrpcerr)
···
result.ReadmeFileName = xrpcResp.Readme.Filename
result.Readme = xrpcResp.Readme.Contents
}
+
ownerSlashRepo := reporesolver.GetBaseRepoPath(r, &f.Repo)
// redirects tree paths trying to access a blob; in this case the result.Files is unpopulated,
// so we can safely redirect to the "parent" (which is the same file).
if len(result.Files) == 0 && result.Parent == treePath {
+
redirectTo := fmt.Sprintf("/%s/blob/%s/%s", ownerSlashRepo, url.PathEscape(ref), result.Parent)
http.Redirect(w, r, redirectTo, http.StatusFound)
return
}
user := rp.oauth.GetUser(r)
var breadcrumbs [][]string
+
breadcrumbs = append(breadcrumbs, []string{f.Name, fmt.Sprintf("/%s/tree/%s", ownerSlashRepo, url.PathEscape(ref))})
if treePath != "" {
for idx, elem := range strings.Split(treePath, "/") {
breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], url.PathEscape(elem))})
+15 -25
appview/reporesolver/resolver.go
···
"strings"
"github.com/bluesky-social/indigo/atproto/identity"
-
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-chi/chi/v5"
"tangled.org/core/appview/config"
"tangled.org/core/appview/db"
···
return &RepoResolver{config: config, enforcer: enforcer, idResolver: resolver, execer: execer}
}
func (rr *RepoResolver) Resolve(r *http.Request) (*ResolvedRepo, error) {
repo, ok := r.Context().Value("repo").(*models.Repo)
if !ok {
···
}, nil
}
-
func (f *ResolvedRepo) OwnerDid() string {
-
return f.OwnerId.DID.String()
-
}
-
-
func (f *ResolvedRepo) OwnerHandle() string {
-
return f.OwnerId.Handle.String()
-
}
-
-
func (f *ResolvedRepo) OwnerSlashRepo() string {
-
handle := f.OwnerId.Handle
-
-
var p string
-
if handle != "" && !handle.IsInvalidHandle() {
-
p, _ = securejoin.SecureJoin(fmt.Sprintf("@%s", handle), f.Name)
-
} else {
-
p, _ = securejoin.SecureJoin(f.OwnerDid(), f.Name)
-
}
-
-
return p
-
}
-
func (f *ResolvedRepo) Collaborators(ctx context.Context) ([]pages.Collaborator, error) {
repoCollaborators, err := f.rr.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
if err != nil {
···
knot := f.Knot
repoInfo := repoinfo.RepoInfo{
-
OwnerDid: f.OwnerDid(),
-
OwnerHandle: f.OwnerHandle(),
Name: f.Name,
-
Rkey: f.Repo.Rkey,
RepoAt: repoAt,
Description: f.Description,
Website: f.Website,
···
"strings"
"github.com/bluesky-social/indigo/atproto/identity"
"github.com/go-chi/chi/v5"
"tangled.org/core/appview/config"
"tangled.org/core/appview/db"
···
return &RepoResolver{config: config, enforcer: enforcer, idResolver: resolver, execer: execer}
}
+
// NOTE: this... should not even be here. the entire package will be removed in future refactor
+
func GetBaseRepoPath(r *http.Request, repo *models.Repo) string {
+
var (
+
user = chi.URLParam(r, "user")
+
name = chi.URLParam(r, "repo")
+
)
+
if user == "" || name == "" {
+
return repo.DidSlashRepo()
+
}
+
return path.Join(user, name)
+
}
+
func (rr *RepoResolver) Resolve(r *http.Request) (*ResolvedRepo, error) {
repo, ok := r.Context().Value("repo").(*models.Repo)
if !ok {
···
}, nil
}
func (f *ResolvedRepo) Collaborators(ctx context.Context) ([]pages.Collaborator, error) {
repoCollaborators, err := f.rr.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
if err != nil {
···
knot := f.Knot
repoInfo := repoinfo.RepoInfo{
+
OwnerDid: f.OwnerId.DID.String(),
+
OwnerHandle: f.OwnerId.Handle.String(),
Name: f.Name,
+
Rkey: f.Rkey,
RepoAt: repoAt,
Description: f.Description,
Website: f.Website,