···
···
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/appview/pages/markup"
···
"tangled.sh/tangled.sh/core/appview/reporesolver"
"tangled.sh/tangled.sh/core/knotclient"
"tangled.sh/tangled.sh/core/patchutil"
"tangled.sh/tangled.sh/core/types"
"github.com/bluesky-social/indigo/atproto/data"
···
lexutil "github.com/bluesky-social/indigo/lex/util"
-
func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) {
ref := chi.URLParam(r, "ref")
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
result, err := us.Index(f.OwnerDid(), f.RepoName, ref)
log.Println("failed to reach knotserver", err)
···
emails := uniqueEmails(commitsTrunc)
-
user := s.oauth.GetUser(r)
repoInfo := f.RepoInfo(user)
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
log.Printf("failed to get registration key for %s: %s", f.Knot, err)
-
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
-
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
···
var forkInfo *types.ForkInfo
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
-
forkInfo, err = getForkInfo(repoInfo, s, f, user, signedClient)
log.Printf("Failed to fetch fork information: %v", err)
···
-
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
···
BranchesTrunc: branchesTrunc,
-
EmailToDidOrHandle: EmailToDidOrHandle(s, emails),
Languages: repoLanguages,
···
repoInfo repoinfo.RepoInfo,
f *reporesolver.ResolvedRepo,
signedClient *knotclient.SignedClient,
···
-
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, s.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot)
···
-
func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
···
ref := chi.URLParam(r, "ref")
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
tagMap[hash] = append(tagMap[hash], tag.Name)
-
user := s.oauth.GetUser(r)
-
s.pages.RepoLog(w, pages.RepoLogParams{
RepoInfo: f.RepoInfo(user),
RepoLogResponse: *repolog,
-
EmailToDidOrHandle: EmailToDidOrHandle(s, uniqueEmails(repolog.Commits)),
-
func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
-
user := s.oauth.GetUser(r)
-
s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
RepoInfo: f.RepoInfo(user),
-
func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
···
-
user := s.oauth.GetUser(r)
-
s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
RepoInfo: f.RepoInfo(user),
-
user := s.oauth.GetUser(r)
newDescription := r.FormValue("description")
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get client")
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
-
err = db.UpdateDescription(s.db, string(repoAt), newDescription)
log.Println("failed to perferom update-description query", err)
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
···
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey)
-
s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
log.Println("failed to perferom update-description query", err)
-
s.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
newRepoInfo := f.RepoInfo(user)
newRepoInfo.Description = newDescription
-
s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
-
func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
ref := chi.URLParam(r, "ref")
-
if !s.config.Core.Dev {
if !plumbing.IsHash(ref) {
···
-
user := s.oauth.GetUser(r)
-
s.pages.RepoCommit(w, pages.RepoCommitParams{
RepoInfo: f.RepoInfo(user),
RepoCommitResponse: result,
-
EmailToDidOrHandle: EmailToDidOrHandle(s, []string{result.Diff.Commit.Author.Email}),
-
func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
···
ref := chi.URLParam(r, "ref")
treePath := chi.URLParam(r, "*")
-
if !s.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath))
···
-
user := s.oauth.GetUser(r)
var breadcrumbs [][]string
breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
baseTreeLink := path.Join(f.OwnerSlashRepo(), "tree", ref, treePath)
baseBlobLink := path.Join(f.OwnerSlashRepo(), "blob", ref, treePath)
-
s.pages.RepoTree(w, pages.RepoTreeParams{
BreadCrumbs: breadcrumbs,
BaseTreeLink: baseTreeLink,
···
-
func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
-
artifacts, err := db.GetArtifact(s.db, db.FilterEq("repo_at", f.RepoAt))
log.Println("failed grab artifacts", err)
···
-
user := s.oauth.GetUser(r)
-
s.pages.RepoTags(w, pages.RepoTagsParams{
RepoInfo: f.RepoInfo(user),
RepoTagsResponse: *result,
···
-
func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
return strings.Compare(a.Name, b.Name) * -1
-
user := s.oauth.GetUser(r)
-
s.pages.RepoBranches(w, pages.RepoBranchesParams{
RepoInfo: f.RepoInfo(user),
RepoBranchesResponse: *result,
···
-
func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
ref := chi.URLParam(r, "ref")
filePath := chi.URLParam(r, "*")
-
if !s.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
showRendered = r.URL.Query().Get("code") != "true"
-
user := s.oauth.GetUser(r)
-
s.pages.RepoBlob(w, pages.RepoBlobParams{
RepoInfo: f.RepoInfo(user),
RepoBlobResponse: result,
···
-
func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
filePath := chi.URLParam(r, "*")
-
if !s.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
-
func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
collaboratorIdent, err := s.idResolver.ResolveIdent(r.Context(), collaborator)
w.Write([]byte("failed to resolve collaborator did to a handle"))
···
// TODO: create an atproto record for this
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
-
tx, err := s.db.BeginTx(r.Context(), nil)
log.Println("failed to start tx")
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
-
err = s.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
-
err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo())
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
-
err = db.AddCollaborator(s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
-
err = s.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
-
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
// remove record from pds
-
xrpcClient, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
···
log.Printf("failed to delete record: %s", err)
-
s.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.")
log.Println("removed repo record ", f.RepoAt.String())
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
log.Println("removed repo from knot ", f.Knot)
-
tx, err := s.db.BeginTx(r.Context(), nil)
log.Println("failed to start tx")
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
-
err = s.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
// remove collaborator RBAC
-
repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
-
s.pages.Notice(w, "settings-delete", "Failed to remove collaborators")
for _, c := range repoCollaborators {
-
s.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
log.Println("removed collaborators")
-
err = s.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
-
s.pages.Notice(w, "settings-delete", "Failed to update RBAC rules")
err = db.RemoveRepo(tx, f.OwnerDid(), f.RepoName)
-
s.pages.Notice(w, "settings-delete", "Failed to update appview")
log.Println("removed repo from db")
···
-
err = s.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
-
s.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
-
func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
if ksResp.StatusCode != http.StatusNoContent {
-
s.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.")
w.Write([]byte(fmt.Sprint("default branch set to: ", branch)))
-
func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) {
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
// for now, this is just pubkeys
-
user := s.oauth.GetUser(r)
repoCollaborators, err := f.Collaborators(r.Context())
log.Println("failed to get collaborators", err)
···
isCollaboratorInviteAllowed := false
-
ok, err := s.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo())
isCollaboratorInviteAllowed = true
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
-
s.pages.RepoSettings(w, pages.RepoSettingsParams{
RepoInfo: f.RepoInfo(user),
Collaborators: repoCollaborators,
···
-
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, comments, err := db.GetIssueWithComments(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue and comments", err)
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
-
issueOwnerIdent, err := s.idResolver.ResolveIdent(r.Context(), issue.OwnerDid)
log.Println("failed to resolve issue owner", err)
···
for i, comment := range comments {
identsToResolve[i] = comment.OwnerDid
-
resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
-
s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{
RepoInfo: f.RepoInfo(user),
···
-
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
closed := tangled.RepoIssueStateClosed
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
···
log.Println("failed to update issue state", err)
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
-
err = db.CloseIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to close issue", err)
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
log.Println("user is not permitted to close issue")
···
-
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
isIssueOwner := user.Did == issue.OwnerDid
if isCollaborator || isIssueOwner {
-
err := db.ReopenIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to reopen issue", err)
-
s.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
log.Println("user is not the owner of the repo")
···
-
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
body := r.FormValue("body")
-
s.pages.Notice(w, "issue", "Body is required")
commentId := mathrand.IntN(1000000)
-
err := db.NewIssueComment(s.db, &db.Comment{
···
log.Println("failed to create comment", err)
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
createdAt := time.Now().Format(time.RFC3339)
commentIdInt64 := int64(commentId)
-
issueAt, err := db.GetIssueAt(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue at", err)
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
atUri := f.RepoAt.String()
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
log.Println("failed to create comment", err)
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId))
-
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
-
identity, err := s.idResolver.ResolveIdent(r.Context(), comment.OwnerDid)
log.Println("failed to resolve did")
···
didHandleMap[identity.DID.String()] = identity.DID.String()
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
-
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
···
-
s.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{
RepoInfo: f.RepoInfo(user),
···
newBody := r.FormValue("body")
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
-
err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody)
log.Println("failed to perferom update-description query", err)
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
···
-
s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json
···
// return new comment body with htmx
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
-
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
···
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
···
-
err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
log.Println("failed to delete comment")
-
s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment")
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
-
s.pages.Notice(w, "issue-comment", "Failed to delete comment.")
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
···
comment.Deleted = &deleted
// htmx fragment of comment after deletion
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
-
func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) {
state := params.Get("state")
···
page = pagination.FirstPage()
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
-
issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page)
log.Println("failed to get issues", err)
-
s.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
for i, issue := range issues {
identsToResolve[i] = issue.OwnerDid
-
resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
-
s.pages.RepoIssues(w, pages.RepoIssuesParams{
-
LoggedInUser: s.oauth.GetUser(r),
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
-
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
-
s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{
RepoInfo: f.RepoInfo(user),
···
body := r.FormValue("body")
if title == "" || body == "" {
-
s.pages.Notice(w, "issues", "Title and body are required")
-
tx, err := s.db.BeginTx(r.Context(), nil)
-
s.pages.Notice(w, "issues", "Failed to create issue, try again later")
···
log.Println("failed to create issue", err)
-
s.pages.Notice(w, "issues", "Failed to create issue.")
-
issueId, err := db.GetIssueId(s.db, f.RepoAt)
log.Println("failed to get issue id", err)
-
s.pages.Notice(w, "issues", "Failed to create issue.")
-
client, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
-
s.pages.Notice(w, "issues", "Failed to create issue.")
atUri := f.RepoAt.String()
···
log.Println("failed to create issue", err)
-
s.pages.Notice(w, "issues", "Failed to create issue.")
-
err = db.SetIssueAt(s.db, f.RepoAt, issueId, resp.Uri)
log.Println("failed to set issue at", err)
-
s.pages.Notice(w, "issues", "Failed to create issue.")
-
if !s.config.Core.Dev {
-
err = s.posthog.Enqueue(posthog.Capture{
Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId},
···
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
-
func (s *State) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Printf("failed to resolve source repo: %v", err)
···
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
-
s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot))
-
client, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
-
s.pages.Notice(w, "repo", "Failed to reach knot server.")
···
_, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref)
-
s.pages.Notice(w, "repo", "Failed to sync repository fork.")
-
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Printf("failed to resolve source repo: %v", err)
···
-
user := s.oauth.GetUser(r)
-
knots, err := s.enforcer.GetDomainsForUser(user.Did)
-
s.pages.Notice(w, "repo", "Invalid user account.")
-
s.pages.ForkRepo(w, pages.ForkRepoParams{
RepoInfo: f.RepoInfo(user),
···
knot := r.FormValue("knot")
-
s.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.")
-
ok, err := s.enforcer.E.Enforce(user.Did, knot, knot, "repo:create")
-
s.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.")
···
// this check is *only* to see if the forked repo name already exists
// in the user's account.
-
existingRepo, err := db.GetRepo(s.db, user.Did, f.RepoName)
if errors.Is(err, sql.ErrNoRows) {
// no existing repo with this name found, we can use the name as is
log.Println("error fetching existing repo from db", err)
-
s.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
} else if existingRepo != nil {
// repo with this name already exists, append random string
forkName = fmt.Sprintf("%s-%s", forkName, randomString(3))
-
secret, err := db.GetRegistrationKey(s.db, knot)
-
s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot))
-
client, err := knotclient.NewSignedClient(knot, secret, s.config.Core.Dev)
-
s.pages.Notice(w, "repo", "Failed to reach knot server.")
···
-
tx, err := s.db.BeginTx(r.Context(), nil)
-
s.pages.Notice(w, "repo", "Failed to save repository information.")
-
err = s.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
···
resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName)
-
s.pages.Notice(w, "repo", "Failed to create repository on knot server.")
case http.StatusConflict:
-
s.pages.Notice(w, "repo", "A repository with that name already exists.")
case http.StatusInternalServerError:
-
s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
case http.StatusNoContent:
-
xrpcClient, err := s.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
-
s.pages.Notice(w, "repo", "Failed to create repository.")
···
log.Printf("failed to create record: %s", err)
-
s.pages.Notice(w, "repo", "Failed to announce repository creation.")
log.Println("created repo record: ", atresp.Uri)
···
err = db.AddRepo(tx, repo)
-
s.pages.Notice(w, "repo", "Failed to save repository information.")
p, _ := securejoin.SecureJoin(user.Did, forkName)
-
err = s.enforcer.AddRepo(user.Did, knot, p)
-
s.pages.Notice(w, "repo", "Failed to set up repository permissions.")
···
-
err = s.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName))
-
func (s *State) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
result, err := us.Branches(f.OwnerDid(), f.RepoName)
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
···
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
repoinfo := f.RepoInfo(user)
-
s.pages.RepoCompareNew(w, pages.RepoCompareNewParams{
···
-
func (s *State) RepoCompare(w http.ResponseWriter, r *http.Request) {
-
user := s.oauth.GetUser(r)
-
f, err := s.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
if base == "" || head == "" {
log.Printf("invalid comparison")
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
branches, err := us.Branches(f.OwnerDid(), f.RepoName)
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
formatPatch, err := us.Compare(f.OwnerDid(), f.RepoName, base, head)
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to compare", err)
···
repoinfo := f.RepoInfo(user)
-
s.pages.RepoCompare(w, pages.RepoCompareParams{
Branches: branches.Branches,
···
···
"tangled.sh/tangled.sh/core/api/tangled"
"tangled.sh/tangled.sh/core/appview"
"tangled.sh/tangled.sh/core/appview/db"
+
"tangled.sh/tangled.sh/core/appview/idresolver"
"tangled.sh/tangled.sh/core/appview/oauth"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/appview/pages/markup"
···
"tangled.sh/tangled.sh/core/appview/reporesolver"
"tangled.sh/tangled.sh/core/knotclient"
"tangled.sh/tangled.sh/core/patchutil"
+
"tangled.sh/tangled.sh/core/rbac"
"tangled.sh/tangled.sh/core/types"
"github.com/bluesky-social/indigo/atproto/data"
···
lexutil "github.com/bluesky-social/indigo/lex/util"
+
repoResolver *reporesolver.RepoResolver
+
idResolver *idresolver.Resolver
+
enforcer *rbac.Enforcer
+
repoResolver *reporesolver.RepoResolver,
+
idResolver *idresolver.Resolver,
+
config *appview.Config,
+
posthog posthog.Client,
+
enforcer *rbac.Enforcer,
+
return &Repo{oauth: oauth,
+
repoResolver: repoResolver,
+
idResolver: idResolver,
+
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
ref := chi.URLParam(r, "ref")
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
result, err := us.Index(f.OwnerDid(), f.RepoName, ref)
log.Println("failed to reach knotserver", err)
···
emails := uniqueEmails(commitsTrunc)
+
user := rp.oauth.GetUser(r)
repoInfo := f.RepoInfo(user)
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
log.Printf("failed to get registration key for %s: %s", f.Knot, err)
+
rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
+
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
···
var forkInfo *types.ForkInfo
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
+
forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient)
log.Printf("Failed to fetch fork information: %v", err)
···
+
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
···
BranchesTrunc: branchesTrunc,
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, emails),
Languages: repoLanguages,
···
repoInfo repoinfo.RepoInfo,
f *reporesolver.ResolvedRepo,
signedClient *knotclient.SignedClient,
···
+
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot)
···
+
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
···
ref := chi.URLParam(r, "ref")
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
tagMap[hash] = append(tagMap[hash], tag.Name)
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoLog(w, pages.RepoLogParams{
RepoInfo: f.RepoInfo(user),
RepoLogResponse: *repolog,
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, uniqueEmails(repolog.Commits)),
+
func (rp *Repo) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
+
user := rp.oauth.GetUser(r)
+
rp.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
RepoInfo: f.RepoInfo(user),
+
func (rp *Repo) RepoDescription(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
w.WriteHeader(http.StatusBadRequest)
···
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
RepoInfo: f.RepoInfo(user),
+
user := rp.oauth.GetUser(r)
newDescription := r.FormValue("description")
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get client")
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
+
err = db.UpdateDescription(rp.db, string(repoAt), newDescription)
log.Println("failed to perferom update-description query", err)
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
···
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey)
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
log.Println("failed to perferom update-description query", err)
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
newRepoInfo := f.RepoInfo(user)
newRepoInfo.Description = newDescription
+
rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
+
func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
ref := chi.URLParam(r, "ref")
+
if !rp.config.Core.Dev {
if !plumbing.IsHash(ref) {
···
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoCommit(w, pages.RepoCommitParams{
RepoInfo: f.RepoInfo(user),
RepoCommitResponse: result,
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, []string{result.Diff.Commit.Author.Email}),
+
func (rp *Repo) RepoTree(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to fully resolve repo", err)
···
ref := chi.URLParam(r, "ref")
treePath := chi.URLParam(r, "*")
+
if !rp.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath))
···
+
user := rp.oauth.GetUser(r)
var breadcrumbs [][]string
breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
baseTreeLink := path.Join(f.OwnerSlashRepo(), "tree", ref, treePath)
baseBlobLink := path.Join(f.OwnerSlashRepo(), "blob", ref, treePath)
+
rp.pages.RepoTree(w, pages.RepoTreeParams{
BreadCrumbs: breadcrumbs,
BaseTreeLink: baseTreeLink,
···
+
func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
+
artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt))
log.Println("failed grab artifacts", err)
···
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoTags(w, pages.RepoTagsParams{
RepoInfo: f.RepoInfo(user),
RepoTagsResponse: *result,
···
+
func (rp *Repo) RepoBranches(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
return strings.Compare(a.Name, b.Name) * -1
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoBranches(w, pages.RepoBranchesParams{
RepoInfo: f.RepoInfo(user),
RepoBranchesResponse: *result,
···
+
func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
ref := chi.URLParam(r, "ref")
filePath := chi.URLParam(r, "*")
+
if !rp.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
showRendered = r.URL.Query().Get("code") != "true"
+
user := rp.oauth.GetUser(r)
+
rp.pages.RepoBlob(w, pages.RepoBlobParams{
RepoInfo: f.RepoInfo(user),
RepoBlobResponse: result,
···
+
func (rp *Repo) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
filePath := chi.URLParam(r, "*")
+
if !rp.config.Core.Dev {
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
+
func (rp *Repo) AddCollaborator(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
collaboratorIdent, err := rp.idResolver.ResolveIdent(r.Context(), collaborator)
w.Write([]byte("failed to resolve collaborator did to a handle"))
···
// TODO: create an atproto record for this
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
+
tx, err := rp.db.BeginTx(r.Context(), nil)
log.Println("failed to start tx")
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
+
err = rp.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
+
err = rp.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo())
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
+
err = db.AddCollaborator(rp.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
+
err = rp.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
···
+
func (rp *Repo) DeleteRepo(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
// remove record from pds
+
xrpcClient, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
···
log.Printf("failed to delete record: %s", err)
+
rp.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.")
log.Println("removed repo record ", f.RepoAt.String())
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
log.Println("removed repo from knot ", f.Knot)
+
tx, err := rp.db.BeginTx(r.Context(), nil)
log.Println("failed to start tx")
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
+
err = rp.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
// remove collaborator RBAC
+
repoCollaborators, err := rp.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
+
rp.pages.Notice(w, "settings-delete", "Failed to remove collaborators")
for _, c := range repoCollaborators {
+
rp.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
log.Println("removed collaborators")
+
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
+
rp.pages.Notice(w, "settings-delete", "Failed to update RBAC rules")
err = db.RemoveRepo(tx, f.OwnerDid(), f.RepoName)
+
rp.pages.Notice(w, "settings-delete", "Failed to update appview")
log.Println("removed repo from db")
···
+
err = rp.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
+
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
+
func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
log.Println("failed to create client to ", f.Knot)
···
if ksResp.StatusCode != http.StatusNoContent {
+
rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.")
w.Write([]byte(fmt.Sprint("default branch set to: ", branch)))
+
func (rp *Repo) RepoSettings(w http.ResponseWriter, r *http.Request) {
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
// for now, this is just pubkeys
+
user := rp.oauth.GetUser(r)
repoCollaborators, err := f.Collaborators(r.Context())
log.Println("failed to get collaborators", err)
···
isCollaboratorInviteAllowed := false
+
ok, err := rp.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo())
isCollaboratorInviteAllowed = true
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Println("failed to create unsigned client", err)
···
+
rp.pages.RepoSettings(w, pages.RepoSettingsParams{
RepoInfo: f.RepoInfo(user),
Collaborators: repoCollaborators,
···
+
func (rp *Repo) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, comments, err := db.GetIssueWithComments(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue and comments", err)
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
+
issueOwnerIdent, err := rp.idResolver.ResolveIdent(r.Context(), issue.OwnerDid)
log.Println("failed to resolve issue owner", err)
···
for i, comment := range comments {
identsToResolve[i] = comment.OwnerDid
+
resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
+
rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{
RepoInfo: f.RepoInfo(user),
···
+
func (rp *Repo) CloseIssue(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
closed := tangled.RepoIssueStateClosed
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
···
log.Println("failed to update issue state", err)
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
+
err = db.CloseIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to close issue", err)
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
log.Println("user is not permitted to close issue")
···
+
func (rp *Repo) ReopenIssue(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
···
isIssueOwner := user.Did == issue.OwnerDid
if isCollaborator || isIssueOwner {
+
err := db.ReopenIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to reopen issue", err)
+
rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
log.Println("user is not the owner of the repo")
···
+
func (rp *Repo) NewIssueComment(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
body := r.FormValue("body")
+
rp.pages.Notice(w, "issue", "Body is required")
commentId := mathrand.IntN(1000000)
+
err := db.NewIssueComment(rp.db, &db.Comment{
···
log.Println("failed to create comment", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
createdAt := time.Now().Format(time.RFC3339)
commentIdInt64 := int64(commentId)
+
issueAt, err := db.GetIssueAt(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue at", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
atUri := f.RepoAt.String()
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
log.Println("failed to create comment", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId))
+
func (rp *Repo) IssueComment(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
+
identity, err := rp.idResolver.ResolveIdent(r.Context(), comment.OwnerDid)
log.Println("failed to resolve did")
···
didHandleMap[identity.DID.String()] = identity.DID.String()
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
+
func (rp *Repo) EditIssueComment(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
···
+
rp.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{
RepoInfo: f.RepoInfo(user),
···
newBody := r.FormValue("body")
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
+
err = db.EditComment(rp.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody)
log.Println("failed to perferom update-description query", err)
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
···
+
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json
···
// return new comment body with htmx
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
+
func (rp *Repo) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
log.Println("failed to get issue", err)
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
···
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
http.Error(w, "bad comment id", http.StatusBadRequest)
···
+
err = db.DeleteComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
log.Println("failed to delete comment")
+
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment")
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
+
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
···
comment.Deleted = &deleted
// htmx fragment of comment after deletion
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
+
func (rp *Repo) RepoIssues(w http.ResponseWriter, r *http.Request) {
state := params.Get("state")
···
page = pagination.FirstPage()
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
+
issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page)
log.Println("failed to get issues", err)
+
rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
···
for i, issue := range issues {
identsToResolve[i] = issue.OwnerDid
+
resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve)
didHandleMap := make(map[string]string)
for _, identity := range resolvedIds {
if !identity.Handle.IsInvalidHandle() {
···
+
rp.pages.RepoIssues(w, pages.RepoIssuesParams{
+
LoggedInUser: rp.oauth.GetUser(r),
RepoInfo: f.RepoInfo(user),
DidHandleMap: didHandleMap,
···
+
func (rp *Repo) NewIssue(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
+
rp.pages.RepoNewIssue(w, pages.RepoNewIssueParams{
RepoInfo: f.RepoInfo(user),
···
body := r.FormValue("body")
if title == "" || body == "" {
+
rp.pages.Notice(w, "issues", "Title and body are required")
+
tx, err := rp.db.BeginTx(r.Context(), nil)
+
rp.pages.Notice(w, "issues", "Failed to create issue, try again later")
···
log.Println("failed to create issue", err)
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
+
issueId, err := db.GetIssueId(rp.db, f.RepoAt)
log.Println("failed to get issue id", err)
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
+
client, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
atUri := f.RepoAt.String()
···
log.Println("failed to create issue", err)
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
+
err = db.SetIssueAt(rp.db, f.RepoAt, issueId, resp.Uri)
log.Println("failed to set issue at", err)
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
+
if !rp.config.Core.Dev {
+
err = rp.posthog.Enqueue(posthog.Capture{
Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId},
···
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
+
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Printf("failed to resolve source repo: %v", err)
···
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
+
rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", f.Knot))
+
client, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
+
rp.pages.Notice(w, "repo", "Failed to reach knot server.")
+
if rp.config.Core.Dev {
···
_, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref)
+
rp.pages.Notice(w, "repo", "Failed to sync repository fork.")
+
func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Printf("failed to resolve source repo: %v", err)
···
+
user := rp.oauth.GetUser(r)
+
knots, err := rp.enforcer.GetDomainsForUser(user.Did)
+
rp.pages.Notice(w, "repo", "Invalid user account.")
+
rp.pages.ForkRepo(w, pages.ForkRepoParams{
RepoInfo: f.RepoInfo(user),
···
knot := r.FormValue("knot")
+
rp.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.")
+
ok, err := rp.enforcer.E.Enforce(user.Did, knot, knot, "repo:create")
+
rp.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.")
···
// this check is *only* to see if the forked repo name already exists
// in the user's account.
+
existingRepo, err := db.GetRepo(rp.db, user.Did, f.RepoName)
if errors.Is(err, sql.ErrNoRows) {
// no existing repo with this name found, we can use the name as is
log.Println("error fetching existing repo from db", err)
+
rp.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
} else if existingRepo != nil {
// repo with this name already exists, append random string
forkName = fmt.Sprintf("%s-%s", forkName, randomString(3))
+
secret, err := db.GetRegistrationKey(rp.db, knot)
+
rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", knot))
+
client, err := knotclient.NewSignedClient(knot, secret, rp.config.Core.Dev)
+
rp.pages.Notice(w, "repo", "Failed to reach knot server.")
+
if rp.config.Core.Dev {
···
+
tx, err := rp.db.BeginTx(r.Context(), nil)
+
rp.pages.Notice(w, "repo", "Failed to save repository information.")
+
err = rp.enforcer.E.LoadPolicy()
log.Println("failed to rollback policies")
···
resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName)
+
rp.pages.Notice(w, "repo", "Failed to create repository on knot server.")
case http.StatusConflict:
+
rp.pages.Notice(w, "repo", "A repository with that name already exists.")
case http.StatusInternalServerError:
+
rp.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
case http.StatusNoContent:
+
xrpcClient, err := rp.oauth.AuthorizedClient(r)
log.Println("failed to get authorized client", err)
+
rp.pages.Notice(w, "repo", "Failed to create repository.")
···
log.Printf("failed to create record: %s", err)
+
rp.pages.Notice(w, "repo", "Failed to announce repository creation.")
log.Println("created repo record: ", atresp.Uri)
···
err = db.AddRepo(tx, repo)
+
rp.pages.Notice(w, "repo", "Failed to save repository information.")
p, _ := securejoin.SecureJoin(user.Did, forkName)
+
err = rp.enforcer.AddRepo(user.Did, knot, p)
+
rp.pages.Notice(w, "repo", "Failed to set up repository permissions.")
···
+
err = rp.enforcer.E.SavePolicy()
log.Println("failed to update ACLs", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
+
rp.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName))
+
func (rp *Repo) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
result, err := us.Branches(f.OwnerDid(), f.RepoName)
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
···
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
repoinfo := f.RepoInfo(user)
+
rp.pages.RepoCompareNew(w, pages.RepoCompareNewParams{
···
+
func (rp *Repo) RepoCompare(w http.ResponseWriter, r *http.Request) {
+
user := rp.oauth.GetUser(r)
+
f, err := rp.repoResolver.Resolve(r)
log.Println("failed to get repo and knot", err)
···
if base == "" || head == "" {
log.Printf("invalid comparison")
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
log.Printf("failed to create unsigned client for %s", f.Knot)
branches, err := us.Branches(f.OwnerDid(), f.RepoName)
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to reach knotserver", err)
formatPatch, err := us.Compare(f.OwnerDid(), f.RepoName, base, head)
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
log.Println("failed to compare", err)
···
repoinfo := f.RepoInfo(user)
+
rp.pages.RepoCompare(w, pages.RepoCompareParams{
Branches: branches.Branches,