This repository has no description
1package pulls
2
3import (
4 "fmt"
5 "net/http"
6
7 "tangled.org/core/appview/db"
8 "tangled.org/core/appview/models"
9 "tangled.org/core/appview/reporesolver"
10 "tangled.org/core/orm"
11
12 "github.com/bluesky-social/indigo/atproto/syntax"
13)
14
15func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
16 l := s.logger.With("handler", "ClosePull")
17 l.Debug("request")
18
19 user := s.oauth.GetMultiAccountUser(r)
20 if user == nil {
21 l.Error("nil user")
22 s.pages.Notice(w, "pull-action-error", "You must be logged in to close this pull.")
23 return
24 }
25 l = l.With("user", user.Did)
26
27 f, err := s.repoResolver.Resolve(r)
28 if err != nil {
29 l.Error("failed to resolve repo", "err", err)
30 return
31 }
32
33 pull, ok := r.Context().Value("pull").(*models.Pull)
34 if !ok {
35 l.Error("failed to get pull")
36 s.pages.Notice(w, "pull-action-error", "Failed to close pull. Try again later.")
37 return
38 }
39 l = l.With("pull", pull.AtUri(), "pull_id", pull.PullId, "state", pull.State)
40
41 // auth filter: only owner or collaborators can close
42 roles := s.acl.RolesInRepo(r.Context(), f, user.Did)
43 isOwner := roles.IsOwner()
44 isCollaborator := roles.IsCollaborator()
45 isPullAuthor := syntax.DID(user.Did) == pull.OwnerDid
46 isCloseAllowed := isOwner || isCollaborator || isPullAuthor
47 if !isCloseAllowed {
48 l.Error("unauthorized to close pull", "is_owner", isOwner, "is_collaborator", isCollaborator, "is_pull_author", isPullAuthor)
49 s.pages.Notice(w, "pull-action-error", "You are unauthorized to close this pull.")
50 return
51 }
52
53 if err := s.writePullStatusRecord(r, user.Did, pull.AtUri(), models.StateClosed); err != nil {
54 l.Error("failed to write issue state record", "err", err)
55 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
56 return
57 }
58
59 tx, err := s.db.BeginTx(r.Context(), nil)
60 if err != nil {
61 l.Error("failed to start transaction", "err", err)
62 s.pages.Notice(w, "pull-action-error", "Failed to close pull.")
63 return
64 }
65 defer tx.Rollback()
66
67 err = db.ClosePulls(
68 tx,
69 orm.FilterEq("at_uri", pull.AtUri()),
70 )
71 if err != nil {
72 l.Error("failed to close pulls in database", "err", err)
73 s.pages.Notice(w, "pull-action-error", "Failed to close pull.")
74 return
75 }
76
77 // Commit the transaction
78 if err = tx.Commit(); err != nil {
79 l.Error("failed to commit transaction", "err", err)
80 s.pages.Notice(w, "pull-action-error", "Failed to close pull.")
81 return
82 }
83
84 s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), pull)
85
86 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f)
87 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
88}
89
90func (s *Pulls) ReopenPull(w http.ResponseWriter, r *http.Request) {
91 l := s.logger.With("handler", "ReopenPull")
92
93 user := s.oauth.GetMultiAccountUser(r)
94 if user == nil {
95 l.Error("nil user")
96 s.pages.Notice(w, "pull-action-error", "You must be logged in to reopen this pull.")
97 return
98 }
99 l = l.With("user", user.Did)
100
101 f, err := s.repoResolver.Resolve(r)
102 if err != nil {
103 l.Error("failed to resolve repo", "err", err)
104 s.pages.Notice(w, "pull-action-error", "Failed to reopen pull.")
105 return
106 }
107
108 pull, ok := r.Context().Value("pull").(*models.Pull)
109 if !ok {
110 l.Error("failed to get pull")
111 s.pages.Notice(w, "pull-action-error", "Failed to reopen pull. Try again later.")
112 return
113 }
114 l = l.With("pull", pull.AtUri(), "pull_id", pull.PullId, "state", pull.State)
115
116 // auth filter: only owner or collaborators can close
117 roles := s.acl.RolesInRepo(r.Context(), f, user.Did)
118 isOwner := roles.IsOwner()
119 isCollaborator := roles.IsCollaborator()
120 isPullAuthor := syntax.DID(user.Did) == pull.OwnerDid
121 isCloseAllowed := isOwner || isCollaborator || isPullAuthor
122 if !isCloseAllowed {
123 l.Error("unauthorized to reopen pull", "is_owner", isOwner, "is_collaborator", isCollaborator, "is_pull_author", isPullAuthor)
124 s.pages.Notice(w, "pull-action-error", "You are unauthorized to reopen this pull.")
125 return
126 }
127
128 if err := s.writePullStatusRecord(r, user.Did, pull.AtUri(), models.StateOpen); err != nil {
129 l.Error("failed to write issue state record", "err", err)
130 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
131 return
132 }
133
134 tx, err := s.db.BeginTx(r.Context(), nil)
135 if err != nil {
136 l.Error("failed to start transaction", "err", err)
137 s.pages.Notice(w, "pull-action-error", "Failed to reopen pull.")
138 return
139 }
140 defer tx.Rollback()
141
142 err = db.ReopenPulls(
143 tx,
144 orm.FilterEq("at_uri", pull.AtUri()),
145 )
146 if err != nil {
147 l.Error("failed to reopen pulls in database", "err", err)
148 s.pages.Notice(w, "pull-action-error", "Failed to reopen pull.")
149 return
150 }
151
152 // Commit the transaction
153 if err = tx.Commit(); err != nil {
154 l.Error("failed to commit transaction", "err", err)
155 s.pages.Notice(w, "pull-action-error", "Failed to reopen pull.")
156 return
157 }
158
159 s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), pull)
160
161 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f)
162 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
163}