+16
-2
appview/pulls/pulls.go
+16
-2
appview/pulls/pulls.go
···
43
43
posthog posthog.Client
44
44
}
45
45
46
-
func New(oauth *oauth.OAuth, repoResolver *reporesolver.RepoResolver, pages *pages.Pages, resolver *idresolver.Resolver, db *db.DB, config *appview.Config) *Pulls {
47
-
return &Pulls{oauth: oauth, repoResolver: repoResolver, pages: pages, idResolver: resolver, db: db, config: config}
46
+
func New(
47
+
oauth *oauth.OAuth,
48
+
repoResolver *reporesolver.RepoResolver,
49
+
pages *pages.Pages,
50
+
resolver *idresolver.Resolver,
51
+
db *db.DB,
52
+
config *appview.Config,
53
+
) *Pulls {
54
+
return &Pulls{
55
+
oauth: oauth,
56
+
repoResolver: repoResolver,
57
+
pages: pages,
58
+
idResolver: resolver,
59
+
db: db,
60
+
config: config,
61
+
}
48
62
}
49
63
50
64
// htmx fragment
+100
appview/repo/router.go
+100
appview/repo/router.go
···
1
+
package repo
2
+
3
+
import (
4
+
"net/http"
5
+
6
+
"github.com/go-chi/chi/v5"
7
+
"tangled.sh/tangled.sh/core/appview/middleware"
8
+
)
9
+
10
+
func (rp *Repo) Router(mw *middleware.Middleware) http.Handler {
11
+
r := chi.NewRouter()
12
+
r.Get("/", rp.RepoIndex)
13
+
r.Get("/commits/{ref}", rp.RepoLog)
14
+
r.Route("/tree/{ref}", func(r chi.Router) {
15
+
r.Get("/", rp.RepoIndex)
16
+
r.Get("/*", rp.RepoTree)
17
+
})
18
+
r.Get("/commit/{ref}", rp.RepoCommit)
19
+
r.Get("/branches", rp.RepoBranches)
20
+
r.Route("/tags", func(r chi.Router) {
21
+
r.Get("/", rp.RepoTags)
22
+
r.Route("/{tag}", func(r chi.Router) {
23
+
r.Use(middleware.AuthMiddleware(rp.oauth))
24
+
// require auth to download for now
25
+
r.Get("/download/{file}", rp.DownloadArtifact)
26
+
27
+
// require repo:push to upload or delete artifacts
28
+
//
29
+
// additionally: only the uploader can truly delete an artifact
30
+
// (record+blob will live on their pds)
31
+
r.Group(func(r chi.Router) {
32
+
r.With(mw.RepoPermissionMiddleware("repo:push"))
33
+
r.Post("/upload", rp.AttachArtifact)
34
+
r.Delete("/{file}", rp.DeleteArtifact)
35
+
})
36
+
})
37
+
})
38
+
r.Get("/blob/{ref}/*", rp.RepoBlob)
39
+
r.Get("/raw/{ref}/*", rp.RepoBlobRaw)
40
+
41
+
r.Route("/issues", func(r chi.Router) {
42
+
r.With(middleware.Paginate).Get("/", rp.RepoIssues)
43
+
r.Get("/{issue}", rp.RepoSingleIssue)
44
+
45
+
r.Group(func(r chi.Router) {
46
+
r.Use(middleware.AuthMiddleware(rp.oauth))
47
+
r.Get("/new", rp.NewIssue)
48
+
r.Post("/new", rp.NewIssue)
49
+
r.Post("/{issue}/comment", rp.NewIssueComment)
50
+
r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) {
51
+
r.Get("/", rp.IssueComment)
52
+
r.Delete("/", rp.DeleteIssueComment)
53
+
r.Get("/edit", rp.EditIssueComment)
54
+
r.Post("/edit", rp.EditIssueComment)
55
+
})
56
+
r.Post("/{issue}/close", rp.CloseIssue)
57
+
r.Post("/{issue}/reopen", rp.ReopenIssue)
58
+
})
59
+
})
60
+
61
+
r.Route("/fork", func(r chi.Router) {
62
+
r.Use(middleware.AuthMiddleware(rp.oauth))
63
+
r.Get("/", rp.ForkRepo)
64
+
r.Post("/", rp.ForkRepo)
65
+
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) {
66
+
r.Post("/", rp.SyncRepoFork)
67
+
})
68
+
})
69
+
70
+
r.Route("/compare", func(r chi.Router) {
71
+
r.Get("/", rp.RepoCompareNew) // start an new comparison
72
+
73
+
// we have to wildcard here since we want to support GitHub's compare syntax
74
+
// /compare/{ref1}...{ref2}
75
+
// for example:
76
+
// /compare/master...some/feature
77
+
// /compare/master...example.com:another/feature <- this is a fork
78
+
r.Get("/{base}/{head}", rp.RepoCompare)
79
+
r.Get("/*", rp.RepoCompare)
80
+
})
81
+
82
+
// settings routes, needs auth
83
+
r.Group(func(r chi.Router) {
84
+
r.Use(middleware.AuthMiddleware(rp.oauth))
85
+
// repo description can only be edited by owner
86
+
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) {
87
+
r.Put("/", rp.RepoDescription)
88
+
r.Get("/", rp.RepoDescription)
89
+
r.Get("/edit", rp.RepoDescriptionEdit)
90
+
})
91
+
r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) {
92
+
r.Get("/", rp.RepoSettings)
93
+
r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", rp.AddCollaborator)
94
+
r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", rp.DeleteRepo)
95
+
r.Put("/branches/default", rp.SetDefaultBranch)
96
+
})
97
+
})
98
+
99
+
return r
100
+
}
+38
-38
appview/state/artifact.go
appview/repo/artifact.go
+38
-38
appview/state/artifact.go
appview/repo/artifact.go
···
1
-
package state
1
+
package repo
2
2
3
3
import (
4
4
"fmt"
···
23
23
)
24
24
25
25
// TODO: proper statuses here on early exit
26
-
func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) {
27
-
user := s.oauth.GetUser(r)
26
+
func (rp *Repo) AttachArtifact(w http.ResponseWriter, r *http.Request) {
27
+
user := rp.oauth.GetUser(r)
28
28
tagParam := chi.URLParam(r, "tag")
29
-
f, err := s.repoResolver.Resolve(r)
29
+
f, err := rp.repoResolver.Resolve(r)
30
30
if err != nil {
31
31
log.Println("failed to get repo and knot", err)
32
-
s.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution")
32
+
rp.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution")
33
33
return
34
34
}
35
35
36
-
tag, err := s.resolveTag(f, tagParam)
36
+
tag, err := rp.resolveTag(f, tagParam)
37
37
if err != nil {
38
38
log.Println("failed to resolve tag", err)
39
-
s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
39
+
rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
40
40
return
41
41
}
42
42
43
43
file, handler, err := r.FormFile("artifact")
44
44
if err != nil {
45
45
log.Println("failed to upload artifact", err)
46
-
s.pages.Notice(w, "upload", "failed to upload artifact")
46
+
rp.pages.Notice(w, "upload", "failed to upload artifact")
47
47
return
48
48
}
49
49
defer file.Close()
50
50
51
-
client, err := s.oauth.AuthorizedClient(r)
51
+
client, err := rp.oauth.AuthorizedClient(r)
52
52
if err != nil {
53
53
log.Println("failed to get authorized client", err)
54
-
s.pages.Notice(w, "upload", "failed to get authorized client")
54
+
rp.pages.Notice(w, "upload", "failed to get authorized client")
55
55
return
56
56
}
57
57
58
58
uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file)
59
59
if err != nil {
60
60
log.Println("failed to upload blob", err)
61
-
s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.")
61
+
rp.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.")
62
62
return
63
63
}
64
64
···
83
83
})
84
84
if err != nil {
85
85
log.Println("failed to create record", err)
86
-
s.pages.Notice(w, "upload", "Failed to create artifact record. Try again later.")
86
+
rp.pages.Notice(w, "upload", "Failed to create artifact record. Try again later.")
87
87
return
88
88
}
89
89
90
90
log.Println(putRecordResp.Uri)
91
91
92
-
tx, err := s.db.BeginTx(r.Context(), nil)
92
+
tx, err := rp.db.BeginTx(r.Context(), nil)
93
93
if err != nil {
94
94
log.Println("failed to start tx")
95
-
s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
95
+
rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
96
96
return
97
97
}
98
98
defer tx.Rollback()
···
112
112
err = db.AddArtifact(tx, artifact)
113
113
if err != nil {
114
114
log.Println("failed to add artifact record to db", err)
115
-
s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
115
+
rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
116
116
return
117
117
}
118
118
119
119
err = tx.Commit()
120
120
if err != nil {
121
121
log.Println("failed to add artifact record to db")
122
-
s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
122
+
rp.pages.Notice(w, "upload", "Failed to create artifact. Try again later.")
123
123
return
124
124
}
125
125
126
-
s.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{
126
+
rp.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{
127
127
LoggedInUser: user,
128
128
RepoInfo: f.RepoInfo(user),
129
129
Artifact: artifact,
···
131
131
}
132
132
133
133
// TODO: proper statuses here on early exit
134
-
func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) {
134
+
func (rp *Repo) DownloadArtifact(w http.ResponseWriter, r *http.Request) {
135
135
tagParam := chi.URLParam(r, "tag")
136
136
filename := chi.URLParam(r, "file")
137
-
f, err := s.repoResolver.Resolve(r)
137
+
f, err := rp.repoResolver.Resolve(r)
138
138
if err != nil {
139
139
log.Println("failed to get repo and knot", err)
140
140
return
141
141
}
142
142
143
-
tag, err := s.resolveTag(f, tagParam)
143
+
tag, err := rp.resolveTag(f, tagParam)
144
144
if err != nil {
145
145
log.Println("failed to resolve tag", err)
146
-
s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
146
+
rp.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution")
147
147
return
148
148
}
149
149
150
-
client, err := s.oauth.AuthorizedClient(r)
150
+
client, err := rp.oauth.AuthorizedClient(r)
151
151
if err != nil {
152
152
log.Println("failed to get authorized client", err)
153
153
return
154
154
}
155
155
156
156
artifacts, err := db.GetArtifact(
157
-
s.db,
157
+
rp.db,
158
158
db.FilterEq("repo_at", f.RepoAt),
159
159
db.FilterEq("tag", tag.Tag.Hash[:]),
160
160
db.FilterEq("name", filename),
···
181
181
}
182
182
183
183
// TODO: proper statuses here on early exit
184
-
func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) {
185
-
user := s.oauth.GetUser(r)
184
+
func (rp *Repo) DeleteArtifact(w http.ResponseWriter, r *http.Request) {
185
+
user := rp.oauth.GetUser(r)
186
186
tagParam := chi.URLParam(r, "tag")
187
187
filename := chi.URLParam(r, "file")
188
-
f, err := s.repoResolver.Resolve(r)
188
+
f, err := rp.repoResolver.Resolve(r)
189
189
if err != nil {
190
190
log.Println("failed to get repo and knot", err)
191
191
return
192
192
}
193
193
194
-
client, _ := s.oauth.AuthorizedClient(r)
194
+
client, _ := rp.oauth.AuthorizedClient(r)
195
195
196
196
tag := plumbing.NewHash(tagParam)
197
197
198
198
artifacts, err := db.GetArtifact(
199
-
s.db,
199
+
rp.db,
200
200
db.FilterEq("repo_at", f.RepoAt),
201
201
db.FilterEq("tag", tag[:]),
202
202
db.FilterEq("name", filename),
203
203
)
204
204
if err != nil {
205
205
log.Println("failed to get artifacts", err)
206
-
s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
206
+
rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
207
207
return
208
208
}
209
209
if len(artifacts) != 1 {
210
-
s.pages.Notice(w, "remove", "Unable to find artifact.")
210
+
rp.pages.Notice(w, "remove", "Unable to find artifact.")
211
211
return
212
212
}
213
213
···
215
215
216
216
if user.Did != artifact.Did {
217
217
log.Println("user not authorized to delete artifact", err)
218
-
s.pages.Notice(w, "remove", "Unauthorized deletion of artifact.")
218
+
rp.pages.Notice(w, "remove", "Unauthorized deletion of artifact.")
219
219
return
220
220
}
221
221
···
226
226
})
227
227
if err != nil {
228
228
log.Println("failed to get blob from pds", err)
229
-
s.pages.Notice(w, "remove", "Failed to remove blob from PDS.")
229
+
rp.pages.Notice(w, "remove", "Failed to remove blob from PDS.")
230
230
return
231
231
}
232
232
233
-
tx, err := s.db.BeginTx(r.Context(), nil)
233
+
tx, err := rp.db.BeginTx(r.Context(), nil)
234
234
if err != nil {
235
235
log.Println("failed to start tx")
236
-
s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
236
+
rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
237
237
return
238
238
}
239
239
defer tx.Rollback()
···
245
245
)
246
246
if err != nil {
247
247
log.Println("failed to remove artifact record from db", err)
248
-
s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
248
+
rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
249
249
return
250
250
}
251
251
252
252
err = tx.Commit()
253
253
if err != nil {
254
254
log.Println("failed to remove artifact record from db")
255
-
s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
255
+
rp.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.")
256
256
return
257
257
}
258
258
259
259
w.Write([]byte{})
260
260
}
261
261
262
-
func (s *State) resolveTag(f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) {
262
+
func (rp *Repo) resolveTag(f *reporesolver.ResolvedRepo, tagParam string) (*types.TagReference, error) {
263
263
tagParam, err := url.QueryUnescape(tagParam)
264
264
if err != nil {
265
265
return nil, err
266
266
}
267
267
268
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
268
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
269
269
if err != nil {
270
270
return nil, err
271
271
}
+295
-261
appview/state/repo.go
appview/repo/repo.go
+295
-261
appview/state/repo.go
appview/repo/repo.go
···
1
-
package state
1
+
package repo
2
2
3
3
import (
4
4
"database/sql"
···
19
19
"tangled.sh/tangled.sh/core/api/tangled"
20
20
"tangled.sh/tangled.sh/core/appview"
21
21
"tangled.sh/tangled.sh/core/appview/db"
22
+
"tangled.sh/tangled.sh/core/appview/idresolver"
22
23
"tangled.sh/tangled.sh/core/appview/oauth"
23
24
"tangled.sh/tangled.sh/core/appview/pages"
24
25
"tangled.sh/tangled.sh/core/appview/pages/markup"
···
27
28
"tangled.sh/tangled.sh/core/appview/reporesolver"
28
29
"tangled.sh/tangled.sh/core/knotclient"
29
30
"tangled.sh/tangled.sh/core/patchutil"
31
+
"tangled.sh/tangled.sh/core/rbac"
30
32
"tangled.sh/tangled.sh/core/types"
31
33
32
34
"github.com/bluesky-social/indigo/atproto/data"
···
39
41
lexutil "github.com/bluesky-social/indigo/lex/util"
40
42
)
41
43
42
-
func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) {
44
+
type Repo struct {
45
+
repoResolver *reporesolver.RepoResolver
46
+
idResolver *idresolver.Resolver
47
+
config *appview.Config
48
+
oauth *oauth.OAuth
49
+
pages *pages.Pages
50
+
db *db.DB
51
+
enforcer *rbac.Enforcer
52
+
posthog posthog.Client
53
+
}
54
+
55
+
func New(
56
+
oauth *oauth.OAuth,
57
+
repoResolver *reporesolver.RepoResolver,
58
+
pages *pages.Pages,
59
+
idResolver *idresolver.Resolver,
60
+
db *db.DB,
61
+
config *appview.Config,
62
+
posthog posthog.Client,
63
+
enforcer *rbac.Enforcer,
64
+
) *Repo {
65
+
return &Repo{oauth: oauth,
66
+
repoResolver: repoResolver,
67
+
pages: pages,
68
+
idResolver: idResolver,
69
+
config: config,
70
+
db: db,
71
+
posthog: posthog,
72
+
enforcer: enforcer,
73
+
}
74
+
}
75
+
76
+
func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) {
43
77
ref := chi.URLParam(r, "ref")
44
-
f, err := s.repoResolver.Resolve(r)
78
+
f, err := rp.repoResolver.Resolve(r)
45
79
if err != nil {
46
80
log.Println("failed to fully resolve repo", err)
47
81
return
48
82
}
49
83
50
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
84
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
51
85
if err != nil {
52
86
log.Printf("failed to create unsigned client for %s", f.Knot)
53
-
s.pages.Error503(w)
87
+
rp.pages.Error503(w)
54
88
return
55
89
}
56
90
57
91
result, err := us.Index(f.OwnerDid(), f.RepoName, ref)
58
92
if err != nil {
59
-
s.pages.Error503(w)
93
+
rp.pages.Error503(w)
60
94
log.Println("failed to reach knotserver", err)
61
95
return
62
96
}
···
107
141
108
142
emails := uniqueEmails(commitsTrunc)
109
143
110
-
user := s.oauth.GetUser(r)
144
+
user := rp.oauth.GetUser(r)
111
145
repoInfo := f.RepoInfo(user)
112
146
113
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
147
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
114
148
if err != nil {
115
149
log.Printf("failed to get registration key for %s: %s", f.Knot, err)
116
-
s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
150
+
rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.")
117
151
}
118
152
119
-
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
153
+
signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
120
154
if err != nil {
121
155
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
122
156
return
···
124
158
125
159
var forkInfo *types.ForkInfo
126
160
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
127
-
forkInfo, err = getForkInfo(repoInfo, s, f, user, signedClient)
161
+
forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient)
128
162
if err != nil {
129
163
log.Printf("Failed to fetch fork information: %v", err)
130
164
return
···
137
171
// non-fatal
138
172
}
139
173
140
-
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
174
+
rp.pages.RepoIndexPage(w, pages.RepoIndexParams{
141
175
LoggedInUser: user,
142
176
RepoInfo: repoInfo,
143
177
TagMap: tagMap,
···
146
180
TagsTrunc: tagsTrunc,
147
181
ForkInfo: forkInfo,
148
182
BranchesTrunc: branchesTrunc,
149
-
EmailToDidOrHandle: EmailToDidOrHandle(s, emails),
183
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, emails),
150
184
Languages: repoLanguages,
151
185
})
152
186
return
···
154
188
155
189
func getForkInfo(
156
190
repoInfo repoinfo.RepoInfo,
157
-
s *State,
191
+
rp *Repo,
158
192
f *reporesolver.ResolvedRepo,
159
193
user *oauth.User,
160
194
signedClient *knotclient.SignedClient,
···
173
207
return &forkInfo, nil
174
208
}
175
209
176
-
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, s.config.Core.Dev)
210
+
us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev)
177
211
if err != nil {
178
212
log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot)
179
213
return nil, err
···
216
250
return &forkInfo, nil
217
251
}
218
252
219
-
func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) {
220
-
f, err := s.repoResolver.Resolve(r)
253
+
func (rp *Repo) RepoLog(w http.ResponseWriter, r *http.Request) {
254
+
f, err := rp.repoResolver.Resolve(r)
221
255
if err != nil {
222
256
log.Println("failed to fully resolve repo", err)
223
257
return
···
233
267
234
268
ref := chi.URLParam(r, "ref")
235
269
236
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
270
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
237
271
if err != nil {
238
272
log.Println("failed to create unsigned client", err)
239
273
return
···
260
294
tagMap[hash] = append(tagMap[hash], tag.Name)
261
295
}
262
296
263
-
user := s.oauth.GetUser(r)
264
-
s.pages.RepoLog(w, pages.RepoLogParams{
297
+
user := rp.oauth.GetUser(r)
298
+
rp.pages.RepoLog(w, pages.RepoLogParams{
265
299
LoggedInUser: user,
266
300
TagMap: tagMap,
267
301
RepoInfo: f.RepoInfo(user),
268
302
RepoLogResponse: *repolog,
269
-
EmailToDidOrHandle: EmailToDidOrHandle(s, uniqueEmails(repolog.Commits)),
303
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, uniqueEmails(repolog.Commits)),
270
304
})
271
305
return
272
306
}
273
307
274
-
func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
275
-
f, err := s.repoResolver.Resolve(r)
308
+
func (rp *Repo) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) {
309
+
f, err := rp.repoResolver.Resolve(r)
276
310
if err != nil {
277
311
log.Println("failed to get repo and knot", err)
278
312
w.WriteHeader(http.StatusBadRequest)
279
313
return
280
314
}
281
315
282
-
user := s.oauth.GetUser(r)
283
-
s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
316
+
user := rp.oauth.GetUser(r)
317
+
rp.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{
284
318
RepoInfo: f.RepoInfo(user),
285
319
})
286
320
return
287
321
}
288
322
289
-
func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) {
290
-
f, err := s.repoResolver.Resolve(r)
323
+
func (rp *Repo) RepoDescription(w http.ResponseWriter, r *http.Request) {
324
+
f, err := rp.repoResolver.Resolve(r)
291
325
if err != nil {
292
326
log.Println("failed to get repo and knot", err)
293
327
w.WriteHeader(http.StatusBadRequest)
···
302
336
return
303
337
}
304
338
305
-
user := s.oauth.GetUser(r)
339
+
user := rp.oauth.GetUser(r)
306
340
307
341
switch r.Method {
308
342
case http.MethodGet:
309
-
s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
343
+
rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
310
344
RepoInfo: f.RepoInfo(user),
311
345
})
312
346
return
313
347
case http.MethodPut:
314
-
user := s.oauth.GetUser(r)
348
+
user := rp.oauth.GetUser(r)
315
349
newDescription := r.FormValue("description")
316
-
client, err := s.oauth.AuthorizedClient(r)
350
+
client, err := rp.oauth.AuthorizedClient(r)
317
351
if err != nil {
318
352
log.Println("failed to get client")
319
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
353
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
320
354
return
321
355
}
322
356
323
357
// optimistic update
324
-
err = db.UpdateDescription(s.db, string(repoAt), newDescription)
358
+
err = db.UpdateDescription(rp.db, string(repoAt), newDescription)
325
359
if err != nil {
326
360
log.Println("failed to perferom update-description query", err)
327
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
361
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
328
362
return
329
363
}
330
364
···
334
368
ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey)
335
369
if err != nil {
336
370
// failed to get record
337
-
s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
371
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.")
338
372
return
339
373
}
340
374
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
356
390
if err != nil {
357
391
log.Println("failed to perferom update-description query", err)
358
392
// failed to get record
359
-
s.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
393
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.")
360
394
return
361
395
}
362
396
363
397
newRepoInfo := f.RepoInfo(user)
364
398
newRepoInfo.Description = newDescription
365
399
366
-
s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
400
+
rp.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{
367
401
RepoInfo: newRepoInfo,
368
402
})
369
403
return
370
404
}
371
405
}
372
406
373
-
func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) {
374
-
f, err := s.repoResolver.Resolve(r)
407
+
func (rp *Repo) RepoCommit(w http.ResponseWriter, r *http.Request) {
408
+
f, err := rp.repoResolver.Resolve(r)
375
409
if err != nil {
376
410
log.Println("failed to fully resolve repo", err)
377
411
return
378
412
}
379
413
ref := chi.URLParam(r, "ref")
380
414
protocol := "http"
381
-
if !s.config.Core.Dev {
415
+
if !rp.config.Core.Dev {
382
416
protocol = "https"
383
417
}
384
418
385
419
if !plumbing.IsHash(ref) {
386
-
s.pages.Error404(w)
420
+
rp.pages.Error404(w)
387
421
return
388
422
}
389
423
···
406
440
return
407
441
}
408
442
409
-
user := s.oauth.GetUser(r)
410
-
s.pages.RepoCommit(w, pages.RepoCommitParams{
443
+
user := rp.oauth.GetUser(r)
444
+
rp.pages.RepoCommit(w, pages.RepoCommitParams{
411
445
LoggedInUser: user,
412
446
RepoInfo: f.RepoInfo(user),
413
447
RepoCommitResponse: result,
414
-
EmailToDidOrHandle: EmailToDidOrHandle(s, []string{result.Diff.Commit.Author.Email}),
448
+
EmailToDidOrHandle: EmailToDidOrHandle(rp, []string{result.Diff.Commit.Author.Email}),
415
449
})
416
450
return
417
451
}
418
452
419
-
func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) {
420
-
f, err := s.repoResolver.Resolve(r)
453
+
func (rp *Repo) RepoTree(w http.ResponseWriter, r *http.Request) {
454
+
f, err := rp.repoResolver.Resolve(r)
421
455
if err != nil {
422
456
log.Println("failed to fully resolve repo", err)
423
457
return
···
426
460
ref := chi.URLParam(r, "ref")
427
461
treePath := chi.URLParam(r, "*")
428
462
protocol := "http"
429
-
if !s.config.Core.Dev {
463
+
if !rp.config.Core.Dev {
430
464
protocol = "https"
431
465
}
432
466
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath))
···
455
489
return
456
490
}
457
491
458
-
user := s.oauth.GetUser(r)
492
+
user := rp.oauth.GetUser(r)
459
493
460
494
var breadcrumbs [][]string
461
495
breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)})
···
468
502
baseTreeLink := path.Join(f.OwnerSlashRepo(), "tree", ref, treePath)
469
503
baseBlobLink := path.Join(f.OwnerSlashRepo(), "blob", ref, treePath)
470
504
471
-
s.pages.RepoTree(w, pages.RepoTreeParams{
505
+
rp.pages.RepoTree(w, pages.RepoTreeParams{
472
506
LoggedInUser: user,
473
507
BreadCrumbs: breadcrumbs,
474
508
BaseTreeLink: baseTreeLink,
···
479
513
return
480
514
}
481
515
482
-
func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) {
483
-
f, err := s.repoResolver.Resolve(r)
516
+
func (rp *Repo) RepoTags(w http.ResponseWriter, r *http.Request) {
517
+
f, err := rp.repoResolver.Resolve(r)
484
518
if err != nil {
485
519
log.Println("failed to get repo and knot", err)
486
520
return
487
521
}
488
522
489
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
523
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
490
524
if err != nil {
491
525
log.Println("failed to create unsigned client", err)
492
526
return
···
498
532
return
499
533
}
500
534
501
-
artifacts, err := db.GetArtifact(s.db, db.FilterEq("repo_at", f.RepoAt))
535
+
artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt))
502
536
if err != nil {
503
537
log.Println("failed grab artifacts", err)
504
538
return
···
526
560
}
527
561
}
528
562
529
-
user := s.oauth.GetUser(r)
530
-
s.pages.RepoTags(w, pages.RepoTagsParams{
563
+
user := rp.oauth.GetUser(r)
564
+
rp.pages.RepoTags(w, pages.RepoTagsParams{
531
565
LoggedInUser: user,
532
566
RepoInfo: f.RepoInfo(user),
533
567
RepoTagsResponse: *result,
···
537
571
return
538
572
}
539
573
540
-
func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) {
541
-
f, err := s.repoResolver.Resolve(r)
574
+
func (rp *Repo) RepoBranches(w http.ResponseWriter, r *http.Request) {
575
+
f, err := rp.repoResolver.Resolve(r)
542
576
if err != nil {
543
577
log.Println("failed to get repo and knot", err)
544
578
return
545
579
}
546
580
547
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
581
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
548
582
if err != nil {
549
583
log.Println("failed to create unsigned client", err)
550
584
return
···
573
607
return strings.Compare(a.Name, b.Name) * -1
574
608
})
575
609
576
-
user := s.oauth.GetUser(r)
577
-
s.pages.RepoBranches(w, pages.RepoBranchesParams{
610
+
user := rp.oauth.GetUser(r)
611
+
rp.pages.RepoBranches(w, pages.RepoBranchesParams{
578
612
LoggedInUser: user,
579
613
RepoInfo: f.RepoInfo(user),
580
614
RepoBranchesResponse: *result,
···
582
616
return
583
617
}
584
618
585
-
func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) {
586
-
f, err := s.repoResolver.Resolve(r)
619
+
func (rp *Repo) RepoBlob(w http.ResponseWriter, r *http.Request) {
620
+
f, err := rp.repoResolver.Resolve(r)
587
621
if err != nil {
588
622
log.Println("failed to get repo and knot", err)
589
623
return
···
592
626
ref := chi.URLParam(r, "ref")
593
627
filePath := chi.URLParam(r, "*")
594
628
protocol := "http"
595
-
if !s.config.Core.Dev {
629
+
if !rp.config.Core.Dev {
596
630
protocol = "https"
597
631
}
598
632
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
630
664
showRendered = r.URL.Query().Get("code") != "true"
631
665
}
632
666
633
-
user := s.oauth.GetUser(r)
634
-
s.pages.RepoBlob(w, pages.RepoBlobParams{
667
+
user := rp.oauth.GetUser(r)
668
+
rp.pages.RepoBlob(w, pages.RepoBlobParams{
635
669
LoggedInUser: user,
636
670
RepoInfo: f.RepoInfo(user),
637
671
RepoBlobResponse: result,
···
642
676
return
643
677
}
644
678
645
-
func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
646
-
f, err := s.repoResolver.Resolve(r)
679
+
func (rp *Repo) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
680
+
f, err := rp.repoResolver.Resolve(r)
647
681
if err != nil {
648
682
log.Println("failed to get repo and knot", err)
649
683
return
···
653
687
filePath := chi.URLParam(r, "*")
654
688
655
689
protocol := "http"
656
-
if !s.config.Core.Dev {
690
+
if !rp.config.Core.Dev {
657
691
protocol = "https"
658
692
}
659
693
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
···
686
720
return
687
721
}
688
722
689
-
func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) {
690
-
f, err := s.repoResolver.Resolve(r)
723
+
func (rp *Repo) AddCollaborator(w http.ResponseWriter, r *http.Request) {
724
+
f, err := rp.repoResolver.Resolve(r)
691
725
if err != nil {
692
726
log.Println("failed to get repo and knot", err)
693
727
return
···
699
733
return
700
734
}
701
735
702
-
collaboratorIdent, err := s.idResolver.ResolveIdent(r.Context(), collaborator)
736
+
collaboratorIdent, err := rp.idResolver.ResolveIdent(r.Context(), collaborator)
703
737
if err != nil {
704
738
w.Write([]byte("failed to resolve collaborator did to a handle"))
705
739
return
···
708
742
709
743
// TODO: create an atproto record for this
710
744
711
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
745
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
712
746
if err != nil {
713
747
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
714
748
return
715
749
}
716
750
717
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
751
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
718
752
if err != nil {
719
753
log.Println("failed to create client to ", f.Knot)
720
754
return
···
731
765
return
732
766
}
733
767
734
-
tx, err := s.db.BeginTx(r.Context(), nil)
768
+
tx, err := rp.db.BeginTx(r.Context(), nil)
735
769
if err != nil {
736
770
log.Println("failed to start tx")
737
771
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
739
773
}
740
774
defer func() {
741
775
tx.Rollback()
742
-
err = s.enforcer.E.LoadPolicy()
776
+
err = rp.enforcer.E.LoadPolicy()
743
777
if err != nil {
744
778
log.Println("failed to rollback policies")
745
779
}
746
780
}()
747
781
748
-
err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo())
782
+
err = rp.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo())
749
783
if err != nil {
750
784
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
751
785
return
752
786
}
753
787
754
-
err = db.AddCollaborator(s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
788
+
err = db.AddCollaborator(rp.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot)
755
789
if err != nil {
756
790
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
757
791
return
···
764
798
return
765
799
}
766
800
767
-
err = s.enforcer.E.SavePolicy()
801
+
err = rp.enforcer.E.SavePolicy()
768
802
if err != nil {
769
803
log.Println("failed to update ACLs", err)
770
804
http.Error(w, err.Error(), http.StatusInternalServerError)
···
775
809
776
810
}
777
811
778
-
func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) {
779
-
user := s.oauth.GetUser(r)
812
+
func (rp *Repo) DeleteRepo(w http.ResponseWriter, r *http.Request) {
813
+
user := rp.oauth.GetUser(r)
780
814
781
-
f, err := s.repoResolver.Resolve(r)
815
+
f, err := rp.repoResolver.Resolve(r)
782
816
if err != nil {
783
817
log.Println("failed to get repo and knot", err)
784
818
return
785
819
}
786
820
787
821
// remove record from pds
788
-
xrpcClient, err := s.oauth.AuthorizedClient(r)
822
+
xrpcClient, err := rp.oauth.AuthorizedClient(r)
789
823
if err != nil {
790
824
log.Println("failed to get authorized client", err)
791
825
return
···
798
832
})
799
833
if err != nil {
800
834
log.Printf("failed to delete record: %s", err)
801
-
s.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.")
835
+
rp.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.")
802
836
return
803
837
}
804
838
log.Println("removed repo record ", f.RepoAt.String())
805
839
806
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
840
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
807
841
if err != nil {
808
842
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
809
843
return
810
844
}
811
845
812
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
846
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
813
847
if err != nil {
814
848
log.Println("failed to create client to ", f.Knot)
815
849
return
···
827
861
log.Println("removed repo from knot ", f.Knot)
828
862
}
829
863
830
-
tx, err := s.db.BeginTx(r.Context(), nil)
864
+
tx, err := rp.db.BeginTx(r.Context(), nil)
831
865
if err != nil {
832
866
log.Println("failed to start tx")
833
867
w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err)))
···
835
869
}
836
870
defer func() {
837
871
tx.Rollback()
838
-
err = s.enforcer.E.LoadPolicy()
872
+
err = rp.enforcer.E.LoadPolicy()
839
873
if err != nil {
840
874
log.Println("failed to rollback policies")
841
875
}
842
876
}()
843
877
844
878
// remove collaborator RBAC
845
-
repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
879
+
repoCollaborators, err := rp.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot)
846
880
if err != nil {
847
-
s.pages.Notice(w, "settings-delete", "Failed to remove collaborators")
881
+
rp.pages.Notice(w, "settings-delete", "Failed to remove collaborators")
848
882
return
849
883
}
850
884
for _, c := range repoCollaborators {
851
885
did := c[0]
852
-
s.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
886
+
rp.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo())
853
887
}
854
888
log.Println("removed collaborators")
855
889
856
890
// remove repo RBAC
857
-
err = s.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
891
+
err = rp.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo())
858
892
if err != nil {
859
-
s.pages.Notice(w, "settings-delete", "Failed to update RBAC rules")
893
+
rp.pages.Notice(w, "settings-delete", "Failed to update RBAC rules")
860
894
return
861
895
}
862
896
863
897
// remove repo from db
864
898
err = db.RemoveRepo(tx, f.OwnerDid(), f.RepoName)
865
899
if err != nil {
866
-
s.pages.Notice(w, "settings-delete", "Failed to update appview")
900
+
rp.pages.Notice(w, "settings-delete", "Failed to update appview")
867
901
return
868
902
}
869
903
log.Println("removed repo from db")
···
875
909
return
876
910
}
877
911
878
-
err = s.enforcer.E.SavePolicy()
912
+
err = rp.enforcer.E.SavePolicy()
879
913
if err != nil {
880
914
log.Println("failed to update ACLs", err)
881
915
http.Error(w, err.Error(), http.StatusInternalServerError)
882
916
return
883
917
}
884
918
885
-
s.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
919
+
rp.pages.HxRedirect(w, fmt.Sprintf("/%s", f.OwnerDid()))
886
920
}
887
921
888
-
func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
889
-
f, err := s.repoResolver.Resolve(r)
922
+
func (rp *Repo) SetDefaultBranch(w http.ResponseWriter, r *http.Request) {
923
+
f, err := rp.repoResolver.Resolve(r)
890
924
if err != nil {
891
925
log.Println("failed to get repo and knot", err)
892
926
return
···
898
932
return
899
933
}
900
934
901
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
935
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
902
936
if err != nil {
903
937
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
904
938
return
905
939
}
906
940
907
-
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
941
+
ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
908
942
if err != nil {
909
943
log.Println("failed to create client to ", f.Knot)
910
944
return
···
917
951
}
918
952
919
953
if ksResp.StatusCode != http.StatusNoContent {
920
-
s.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.")
954
+
rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.")
921
955
return
922
956
}
923
957
924
958
w.Write([]byte(fmt.Sprint("default branch set to: ", branch)))
925
959
}
926
960
927
-
func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) {
928
-
f, err := s.repoResolver.Resolve(r)
961
+
func (rp *Repo) RepoSettings(w http.ResponseWriter, r *http.Request) {
962
+
f, err := rp.repoResolver.Resolve(r)
929
963
if err != nil {
930
964
log.Println("failed to get repo and knot", err)
931
965
return
···
934
968
switch r.Method {
935
969
case http.MethodGet:
936
970
// for now, this is just pubkeys
937
-
user := s.oauth.GetUser(r)
971
+
user := rp.oauth.GetUser(r)
938
972
repoCollaborators, err := f.Collaborators(r.Context())
939
973
if err != nil {
940
974
log.Println("failed to get collaborators", err)
···
942
976
943
977
isCollaboratorInviteAllowed := false
944
978
if user != nil {
945
-
ok, err := s.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo())
979
+
ok, err := rp.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.DidSlashRepo())
946
980
if err == nil && ok {
947
981
isCollaboratorInviteAllowed = true
948
982
}
949
983
}
950
984
951
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
985
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
952
986
if err != nil {
953
987
log.Println("failed to create unsigned client", err)
954
988
return
···
960
994
return
961
995
}
962
996
963
-
s.pages.RepoSettings(w, pages.RepoSettingsParams{
997
+
rp.pages.RepoSettings(w, pages.RepoSettingsParams{
964
998
LoggedInUser: user,
965
999
RepoInfo: f.RepoInfo(user),
966
1000
Collaborators: repoCollaborators,
···
970
1004
}
971
1005
}
972
1006
973
-
func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
974
-
user := s.oauth.GetUser(r)
975
-
f, err := s.repoResolver.Resolve(r)
1007
+
func (rp *Repo) RepoSingleIssue(w http.ResponseWriter, r *http.Request) {
1008
+
user := rp.oauth.GetUser(r)
1009
+
f, err := rp.repoResolver.Resolve(r)
976
1010
if err != nil {
977
1011
log.Println("failed to get repo and knot", err)
978
1012
return
···
986
1020
return
987
1021
}
988
1022
989
-
issue, comments, err := db.GetIssueWithComments(s.db, f.RepoAt, issueIdInt)
1023
+
issue, comments, err := db.GetIssueWithComments(rp.db, f.RepoAt, issueIdInt)
990
1024
if err != nil {
991
1025
log.Println("failed to get issue and comments", err)
992
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1026
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
993
1027
return
994
1028
}
995
1029
996
-
issueOwnerIdent, err := s.idResolver.ResolveIdent(r.Context(), issue.OwnerDid)
1030
+
issueOwnerIdent, err := rp.idResolver.ResolveIdent(r.Context(), issue.OwnerDid)
997
1031
if err != nil {
998
1032
log.Println("failed to resolve issue owner", err)
999
1033
}
···
1002
1036
for i, comment := range comments {
1003
1037
identsToResolve[i] = comment.OwnerDid
1004
1038
}
1005
-
resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve)
1039
+
resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve)
1006
1040
didHandleMap := make(map[string]string)
1007
1041
for _, identity := range resolvedIds {
1008
1042
if !identity.Handle.IsInvalidHandle() {
···
1012
1046
}
1013
1047
}
1014
1048
1015
-
s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{
1049
+
rp.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{
1016
1050
LoggedInUser: user,
1017
1051
RepoInfo: f.RepoInfo(user),
1018
1052
Issue: *issue,
···
1024
1058
1025
1059
}
1026
1060
1027
-
func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) {
1028
-
user := s.oauth.GetUser(r)
1029
-
f, err := s.repoResolver.Resolve(r)
1061
+
func (rp *Repo) CloseIssue(w http.ResponseWriter, r *http.Request) {
1062
+
user := rp.oauth.GetUser(r)
1063
+
f, err := rp.repoResolver.Resolve(r)
1030
1064
if err != nil {
1031
1065
log.Println("failed to get repo and knot", err)
1032
1066
return
···
1040
1074
return
1041
1075
}
1042
1076
1043
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
1077
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
1044
1078
if err != nil {
1045
1079
log.Println("failed to get issue", err)
1046
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1080
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1047
1081
return
1048
1082
}
1049
1083
···
1061
1095
1062
1096
closed := tangled.RepoIssueStateClosed
1063
1097
1064
-
client, err := s.oauth.AuthorizedClient(r)
1098
+
client, err := rp.oauth.AuthorizedClient(r)
1065
1099
if err != nil {
1066
1100
log.Println("failed to get authorized client", err)
1067
1101
return
···
1080
1114
1081
1115
if err != nil {
1082
1116
log.Println("failed to update issue state", err)
1083
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1117
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1084
1118
return
1085
1119
}
1086
1120
1087
-
err = db.CloseIssue(s.db, f.RepoAt, issueIdInt)
1121
+
err = db.CloseIssue(rp.db, f.RepoAt, issueIdInt)
1088
1122
if err != nil {
1089
1123
log.Println("failed to close issue", err)
1090
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1124
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1091
1125
return
1092
1126
}
1093
1127
1094
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
1128
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
1095
1129
return
1096
1130
} else {
1097
1131
log.Println("user is not permitted to close issue")
···
1100
1134
}
1101
1135
}
1102
1136
1103
-
func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) {
1104
-
user := s.oauth.GetUser(r)
1105
-
f, err := s.repoResolver.Resolve(r)
1137
+
func (rp *Repo) ReopenIssue(w http.ResponseWriter, r *http.Request) {
1138
+
user := rp.oauth.GetUser(r)
1139
+
f, err := rp.repoResolver.Resolve(r)
1106
1140
if err != nil {
1107
1141
log.Println("failed to get repo and knot", err)
1108
1142
return
···
1116
1150
return
1117
1151
}
1118
1152
1119
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
1153
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
1120
1154
if err != nil {
1121
1155
log.Println("failed to get issue", err)
1122
-
s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1156
+
rp.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.")
1123
1157
return
1124
1158
}
1125
1159
···
1133
1167
isIssueOwner := user.Did == issue.OwnerDid
1134
1168
1135
1169
if isCollaborator || isIssueOwner {
1136
-
err := db.ReopenIssue(s.db, f.RepoAt, issueIdInt)
1170
+
err := db.ReopenIssue(rp.db, f.RepoAt, issueIdInt)
1137
1171
if err != nil {
1138
1172
log.Println("failed to reopen issue", err)
1139
-
s.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
1173
+
rp.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.")
1140
1174
return
1141
1175
}
1142
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
1176
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt))
1143
1177
return
1144
1178
} else {
1145
1179
log.Println("user is not the owner of the repo")
···
1148
1182
}
1149
1183
}
1150
1184
1151
-
func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) {
1152
-
user := s.oauth.GetUser(r)
1153
-
f, err := s.repoResolver.Resolve(r)
1185
+
func (rp *Repo) NewIssueComment(w http.ResponseWriter, r *http.Request) {
1186
+
user := rp.oauth.GetUser(r)
1187
+
f, err := rp.repoResolver.Resolve(r)
1154
1188
if err != nil {
1155
1189
log.Println("failed to get repo and knot", err)
1156
1190
return
···
1168
1202
case http.MethodPost:
1169
1203
body := r.FormValue("body")
1170
1204
if body == "" {
1171
-
s.pages.Notice(w, "issue", "Body is required")
1205
+
rp.pages.Notice(w, "issue", "Body is required")
1172
1206
return
1173
1207
}
1174
1208
1175
1209
commentId := mathrand.IntN(1000000)
1176
1210
rkey := appview.TID()
1177
1211
1178
-
err := db.NewIssueComment(s.db, &db.Comment{
1212
+
err := db.NewIssueComment(rp.db, &db.Comment{
1179
1213
OwnerDid: user.Did,
1180
1214
RepoAt: f.RepoAt,
1181
1215
Issue: issueIdInt,
···
1185
1219
})
1186
1220
if err != nil {
1187
1221
log.Println("failed to create comment", err)
1188
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1222
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
1189
1223
return
1190
1224
}
1191
1225
1192
1226
createdAt := time.Now().Format(time.RFC3339)
1193
1227
commentIdInt64 := int64(commentId)
1194
1228
ownerDid := user.Did
1195
-
issueAt, err := db.GetIssueAt(s.db, f.RepoAt, issueIdInt)
1229
+
issueAt, err := db.GetIssueAt(rp.db, f.RepoAt, issueIdInt)
1196
1230
if err != nil {
1197
1231
log.Println("failed to get issue at", err)
1198
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1232
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
1199
1233
return
1200
1234
}
1201
1235
1202
1236
atUri := f.RepoAt.String()
1203
-
client, err := s.oauth.AuthorizedClient(r)
1237
+
client, err := rp.oauth.AuthorizedClient(r)
1204
1238
if err != nil {
1205
1239
log.Println("failed to get authorized client", err)
1206
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1240
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
1207
1241
return
1208
1242
}
1209
1243
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
···
1223
1257
})
1224
1258
if err != nil {
1225
1259
log.Println("failed to create comment", err)
1226
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1260
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
1227
1261
return
1228
1262
}
1229
1263
1230
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId))
1264
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId))
1231
1265
return
1232
1266
}
1233
1267
}
1234
1268
1235
-
func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) {
1236
-
user := s.oauth.GetUser(r)
1237
-
f, err := s.repoResolver.Resolve(r)
1269
+
func (rp *Repo) IssueComment(w http.ResponseWriter, r *http.Request) {
1270
+
user := rp.oauth.GetUser(r)
1271
+
f, err := rp.repoResolver.Resolve(r)
1238
1272
if err != nil {
1239
1273
log.Println("failed to get repo and knot", err)
1240
1274
return
···
1256
1290
return
1257
1291
}
1258
1292
1259
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
1293
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
1260
1294
if err != nil {
1261
1295
log.Println("failed to get issue", err)
1262
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1296
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1263
1297
return
1264
1298
}
1265
1299
1266
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
1300
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
1267
1301
if err != nil {
1268
1302
http.Error(w, "bad comment id", http.StatusBadRequest)
1269
1303
return
1270
1304
}
1271
1305
1272
-
identity, err := s.idResolver.ResolveIdent(r.Context(), comment.OwnerDid)
1306
+
identity, err := rp.idResolver.ResolveIdent(r.Context(), comment.OwnerDid)
1273
1307
if err != nil {
1274
1308
log.Println("failed to resolve did")
1275
1309
return
···
1282
1316
didHandleMap[identity.DID.String()] = identity.DID.String()
1283
1317
}
1284
1318
1285
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1319
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1286
1320
LoggedInUser: user,
1287
1321
RepoInfo: f.RepoInfo(user),
1288
1322
DidHandleMap: didHandleMap,
···
1291
1325
})
1292
1326
}
1293
1327
1294
-
func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) {
1295
-
user := s.oauth.GetUser(r)
1296
-
f, err := s.repoResolver.Resolve(r)
1328
+
func (rp *Repo) EditIssueComment(w http.ResponseWriter, r *http.Request) {
1329
+
user := rp.oauth.GetUser(r)
1330
+
f, err := rp.repoResolver.Resolve(r)
1297
1331
if err != nil {
1298
1332
log.Println("failed to get repo and knot", err)
1299
1333
return
···
1315
1349
return
1316
1350
}
1317
1351
1318
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
1352
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
1319
1353
if err != nil {
1320
1354
log.Println("failed to get issue", err)
1321
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1355
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1322
1356
return
1323
1357
}
1324
1358
1325
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
1359
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
1326
1360
if err != nil {
1327
1361
http.Error(w, "bad comment id", http.StatusBadRequest)
1328
1362
return
···
1335
1369
1336
1370
switch r.Method {
1337
1371
case http.MethodGet:
1338
-
s.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{
1372
+
rp.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{
1339
1373
LoggedInUser: user,
1340
1374
RepoInfo: f.RepoInfo(user),
1341
1375
Issue: issue,
···
1344
1378
case http.MethodPost:
1345
1379
// extract form value
1346
1380
newBody := r.FormValue("body")
1347
-
client, err := s.oauth.AuthorizedClient(r)
1381
+
client, err := rp.oauth.AuthorizedClient(r)
1348
1382
if err != nil {
1349
1383
log.Println("failed to get authorized client", err)
1350
-
s.pages.Notice(w, "issue-comment", "Failed to create comment.")
1384
+
rp.pages.Notice(w, "issue-comment", "Failed to create comment.")
1351
1385
return
1352
1386
}
1353
1387
rkey := comment.Rkey
1354
1388
1355
1389
// optimistic update
1356
1390
edited := time.Now()
1357
-
err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody)
1391
+
err = db.EditComment(rp.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody)
1358
1392
if err != nil {
1359
1393
log.Println("failed to perferom update-description query", err)
1360
-
s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
1394
+
rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.")
1361
1395
return
1362
1396
}
1363
1397
···
1368
1402
if err != nil {
1369
1403
// failed to get record
1370
1404
log.Println(err, rkey)
1371
-
s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
1405
+
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")
1372
1406
return
1373
1407
}
1374
1408
value, _ := ex.Value.MarshalJSON() // we just did get record; it is valid json
···
1408
1442
comment.Edited = &edited
1409
1443
1410
1444
// return new comment body with htmx
1411
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1445
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1412
1446
LoggedInUser: user,
1413
1447
RepoInfo: f.RepoInfo(user),
1414
1448
DidHandleMap: didHandleMap,
···
1421
1455
1422
1456
}
1423
1457
1424
-
func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
1425
-
user := s.oauth.GetUser(r)
1426
-
f, err := s.repoResolver.Resolve(r)
1458
+
func (rp *Repo) DeleteIssueComment(w http.ResponseWriter, r *http.Request) {
1459
+
user := rp.oauth.GetUser(r)
1460
+
f, err := rp.repoResolver.Resolve(r)
1427
1461
if err != nil {
1428
1462
log.Println("failed to get repo and knot", err)
1429
1463
return
···
1437
1471
return
1438
1472
}
1439
1473
1440
-
issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt)
1474
+
issue, err := db.GetIssue(rp.db, f.RepoAt, issueIdInt)
1441
1475
if err != nil {
1442
1476
log.Println("failed to get issue", err)
1443
-
s.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1477
+
rp.pages.Notice(w, "issues", "Failed to load issue. Try again later.")
1444
1478
return
1445
1479
}
1446
1480
···
1452
1486
return
1453
1487
}
1454
1488
1455
-
comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
1489
+
comment, err := db.GetComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
1456
1490
if err != nil {
1457
1491
http.Error(w, "bad comment id", http.StatusBadRequest)
1458
1492
return
···
1470
1504
1471
1505
// optimistic deletion
1472
1506
deleted := time.Now()
1473
-
err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt)
1507
+
err = db.DeleteComment(rp.db, f.RepoAt, issueIdInt, commentIdInt)
1474
1508
if err != nil {
1475
1509
log.Println("failed to delete comment")
1476
-
s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment")
1510
+
rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment")
1477
1511
return
1478
1512
}
1479
1513
1480
1514
// delete from pds
1481
1515
if comment.Rkey != "" {
1482
-
client, err := s.oauth.AuthorizedClient(r)
1516
+
client, err := rp.oauth.AuthorizedClient(r)
1483
1517
if err != nil {
1484
1518
log.Println("failed to get authorized client", err)
1485
-
s.pages.Notice(w, "issue-comment", "Failed to delete comment.")
1519
+
rp.pages.Notice(w, "issue-comment", "Failed to delete comment.")
1486
1520
return
1487
1521
}
1488
1522
_, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{
···
1503
1537
comment.Deleted = &deleted
1504
1538
1505
1539
// htmx fragment of comment after deletion
1506
-
s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1540
+
rp.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{
1507
1541
LoggedInUser: user,
1508
1542
RepoInfo: f.RepoInfo(user),
1509
1543
DidHandleMap: didHandleMap,
···
1513
1547
return
1514
1548
}
1515
1549
1516
-
func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) {
1550
+
func (rp *Repo) RepoIssues(w http.ResponseWriter, r *http.Request) {
1517
1551
params := r.URL.Query()
1518
1552
state := params.Get("state")
1519
1553
isOpen := true
···
1532
1566
page = pagination.FirstPage()
1533
1567
}
1534
1568
1535
-
user := s.oauth.GetUser(r)
1536
-
f, err := s.repoResolver.Resolve(r)
1569
+
user := rp.oauth.GetUser(r)
1570
+
f, err := rp.repoResolver.Resolve(r)
1537
1571
if err != nil {
1538
1572
log.Println("failed to get repo and knot", err)
1539
1573
return
1540
1574
}
1541
1575
1542
-
issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page)
1576
+
issues, err := db.GetIssues(rp.db, f.RepoAt, isOpen, page)
1543
1577
if err != nil {
1544
1578
log.Println("failed to get issues", err)
1545
-
s.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
1579
+
rp.pages.Notice(w, "issues", "Failed to load issues. Try again later.")
1546
1580
return
1547
1581
}
1548
1582
···
1550
1584
for i, issue := range issues {
1551
1585
identsToResolve[i] = issue.OwnerDid
1552
1586
}
1553
-
resolvedIds := s.idResolver.ResolveIdents(r.Context(), identsToResolve)
1587
+
resolvedIds := rp.idResolver.ResolveIdents(r.Context(), identsToResolve)
1554
1588
didHandleMap := make(map[string]string)
1555
1589
for _, identity := range resolvedIds {
1556
1590
if !identity.Handle.IsInvalidHandle() {
···
1560
1594
}
1561
1595
}
1562
1596
1563
-
s.pages.RepoIssues(w, pages.RepoIssuesParams{
1564
-
LoggedInUser: s.oauth.GetUser(r),
1597
+
rp.pages.RepoIssues(w, pages.RepoIssuesParams{
1598
+
LoggedInUser: rp.oauth.GetUser(r),
1565
1599
RepoInfo: f.RepoInfo(user),
1566
1600
Issues: issues,
1567
1601
DidHandleMap: didHandleMap,
···
1571
1605
return
1572
1606
}
1573
1607
1574
-
func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) {
1575
-
user := s.oauth.GetUser(r)
1608
+
func (rp *Repo) NewIssue(w http.ResponseWriter, r *http.Request) {
1609
+
user := rp.oauth.GetUser(r)
1576
1610
1577
-
f, err := s.repoResolver.Resolve(r)
1611
+
f, err := rp.repoResolver.Resolve(r)
1578
1612
if err != nil {
1579
1613
log.Println("failed to get repo and knot", err)
1580
1614
return
···
1582
1616
1583
1617
switch r.Method {
1584
1618
case http.MethodGet:
1585
-
s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{
1619
+
rp.pages.RepoNewIssue(w, pages.RepoNewIssueParams{
1586
1620
LoggedInUser: user,
1587
1621
RepoInfo: f.RepoInfo(user),
1588
1622
})
···
1591
1625
body := r.FormValue("body")
1592
1626
1593
1627
if title == "" || body == "" {
1594
-
s.pages.Notice(w, "issues", "Title and body are required")
1628
+
rp.pages.Notice(w, "issues", "Title and body are required")
1595
1629
return
1596
1630
}
1597
1631
1598
-
tx, err := s.db.BeginTx(r.Context(), nil)
1632
+
tx, err := rp.db.BeginTx(r.Context(), nil)
1599
1633
if err != nil {
1600
-
s.pages.Notice(w, "issues", "Failed to create issue, try again later")
1634
+
rp.pages.Notice(w, "issues", "Failed to create issue, try again later")
1601
1635
return
1602
1636
}
1603
1637
···
1609
1643
})
1610
1644
if err != nil {
1611
1645
log.Println("failed to create issue", err)
1612
-
s.pages.Notice(w, "issues", "Failed to create issue.")
1646
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
1613
1647
return
1614
1648
}
1615
1649
1616
-
issueId, err := db.GetIssueId(s.db, f.RepoAt)
1650
+
issueId, err := db.GetIssueId(rp.db, f.RepoAt)
1617
1651
if err != nil {
1618
1652
log.Println("failed to get issue id", err)
1619
-
s.pages.Notice(w, "issues", "Failed to create issue.")
1653
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
1620
1654
return
1621
1655
}
1622
1656
1623
-
client, err := s.oauth.AuthorizedClient(r)
1657
+
client, err := rp.oauth.AuthorizedClient(r)
1624
1658
if err != nil {
1625
1659
log.Println("failed to get authorized client", err)
1626
-
s.pages.Notice(w, "issues", "Failed to create issue.")
1660
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
1627
1661
return
1628
1662
}
1629
1663
atUri := f.RepoAt.String()
···
1643
1677
})
1644
1678
if err != nil {
1645
1679
log.Println("failed to create issue", err)
1646
-
s.pages.Notice(w, "issues", "Failed to create issue.")
1680
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
1647
1681
return
1648
1682
}
1649
1683
1650
-
err = db.SetIssueAt(s.db, f.RepoAt, issueId, resp.Uri)
1684
+
err = db.SetIssueAt(rp.db, f.RepoAt, issueId, resp.Uri)
1651
1685
if err != nil {
1652
1686
log.Println("failed to set issue at", err)
1653
-
s.pages.Notice(w, "issues", "Failed to create issue.")
1687
+
rp.pages.Notice(w, "issues", "Failed to create issue.")
1654
1688
return
1655
1689
}
1656
1690
1657
-
if !s.config.Core.Dev {
1658
-
err = s.posthog.Enqueue(posthog.Capture{
1691
+
if !rp.config.Core.Dev {
1692
+
err = rp.posthog.Enqueue(posthog.Capture{
1659
1693
DistinctId: user.Did,
1660
1694
Event: "new_issue",
1661
1695
Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId},
···
1665
1699
}
1666
1700
}
1667
1701
1668
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
1702
+
rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
1669
1703
return
1670
1704
}
1671
1705
}
1672
1706
1673
-
func (s *State) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
1674
-
user := s.oauth.GetUser(r)
1675
-
f, err := s.repoResolver.Resolve(r)
1707
+
func (rp *Repo) SyncRepoFork(w http.ResponseWriter, r *http.Request) {
1708
+
user := rp.oauth.GetUser(r)
1709
+
f, err := rp.repoResolver.Resolve(r)
1676
1710
if err != nil {
1677
1711
log.Printf("failed to resolve source repo: %v", err)
1678
1712
return
···
1680
1714
1681
1715
switch r.Method {
1682
1716
case http.MethodPost:
1683
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
1717
+
secret, err := db.GetRegistrationKey(rp.db, f.Knot)
1684
1718
if err != nil {
1685
-
s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot))
1719
+
rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", f.Knot))
1686
1720
return
1687
1721
}
1688
1722
1689
-
client, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev)
1723
+
client, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev)
1690
1724
if err != nil {
1691
-
s.pages.Notice(w, "repo", "Failed to reach knot server.")
1725
+
rp.pages.Notice(w, "repo", "Failed to reach knot server.")
1692
1726
return
1693
1727
}
1694
1728
1695
1729
var uri string
1696
-
if s.config.Core.Dev {
1730
+
if rp.config.Core.Dev {
1697
1731
uri = "http"
1698
1732
} else {
1699
1733
uri = "https"
···
1703
1737
1704
1738
_, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref)
1705
1739
if err != nil {
1706
-
s.pages.Notice(w, "repo", "Failed to sync repository fork.")
1740
+
rp.pages.Notice(w, "repo", "Failed to sync repository fork.")
1707
1741
return
1708
1742
}
1709
1743
1710
-
s.pages.HxRefresh(w)
1744
+
rp.pages.HxRefresh(w)
1711
1745
return
1712
1746
}
1713
1747
}
1714
1748
1715
-
func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) {
1716
-
user := s.oauth.GetUser(r)
1717
-
f, err := s.repoResolver.Resolve(r)
1749
+
func (rp *Repo) ForkRepo(w http.ResponseWriter, r *http.Request) {
1750
+
user := rp.oauth.GetUser(r)
1751
+
f, err := rp.repoResolver.Resolve(r)
1718
1752
if err != nil {
1719
1753
log.Printf("failed to resolve source repo: %v", err)
1720
1754
return
···
1722
1756
1723
1757
switch r.Method {
1724
1758
case http.MethodGet:
1725
-
user := s.oauth.GetUser(r)
1726
-
knots, err := s.enforcer.GetDomainsForUser(user.Did)
1759
+
user := rp.oauth.GetUser(r)
1760
+
knots, err := rp.enforcer.GetDomainsForUser(user.Did)
1727
1761
if err != nil {
1728
-
s.pages.Notice(w, "repo", "Invalid user account.")
1762
+
rp.pages.Notice(w, "repo", "Invalid user account.")
1729
1763
return
1730
1764
}
1731
1765
1732
-
s.pages.ForkRepo(w, pages.ForkRepoParams{
1766
+
rp.pages.ForkRepo(w, pages.ForkRepoParams{
1733
1767
LoggedInUser: user,
1734
1768
Knots: knots,
1735
1769
RepoInfo: f.RepoInfo(user),
···
1739
1773
1740
1774
knot := r.FormValue("knot")
1741
1775
if knot == "" {
1742
-
s.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.")
1776
+
rp.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.")
1743
1777
return
1744
1778
}
1745
1779
1746
-
ok, err := s.enforcer.E.Enforce(user.Did, knot, knot, "repo:create")
1780
+
ok, err := rp.enforcer.E.Enforce(user.Did, knot, knot, "repo:create")
1747
1781
if err != nil || !ok {
1748
-
s.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.")
1782
+
rp.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.")
1749
1783
return
1750
1784
}
1751
1785
···
1753
1787
1754
1788
// this check is *only* to see if the forked repo name already exists
1755
1789
// in the user's account.
1756
-
existingRepo, err := db.GetRepo(s.db, user.Did, f.RepoName)
1790
+
existingRepo, err := db.GetRepo(rp.db, user.Did, f.RepoName)
1757
1791
if err != nil {
1758
1792
if errors.Is(err, sql.ErrNoRows) {
1759
1793
// no existing repo with this name found, we can use the name as is
1760
1794
} else {
1761
1795
log.Println("error fetching existing repo from db", err)
1762
-
s.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
1796
+
rp.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.")
1763
1797
return
1764
1798
}
1765
1799
} else if existingRepo != nil {
1766
1800
// repo with this name already exists, append random string
1767
1801
forkName = fmt.Sprintf("%s-%s", forkName, randomString(3))
1768
1802
}
1769
-
secret, err := db.GetRegistrationKey(s.db, knot)
1803
+
secret, err := db.GetRegistrationKey(rp.db, knot)
1770
1804
if err != nil {
1771
-
s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot))
1805
+
rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", knot))
1772
1806
return
1773
1807
}
1774
1808
1775
-
client, err := knotclient.NewSignedClient(knot, secret, s.config.Core.Dev)
1809
+
client, err := knotclient.NewSignedClient(knot, secret, rp.config.Core.Dev)
1776
1810
if err != nil {
1777
-
s.pages.Notice(w, "repo", "Failed to reach knot server.")
1811
+
rp.pages.Notice(w, "repo", "Failed to reach knot server.")
1778
1812
return
1779
1813
}
1780
1814
1781
1815
var uri string
1782
-
if s.config.Core.Dev {
1816
+
if rp.config.Core.Dev {
1783
1817
uri = "http"
1784
1818
} else {
1785
1819
uri = "https"
···
1796
1830
Source: sourceAt,
1797
1831
}
1798
1832
1799
-
tx, err := s.db.BeginTx(r.Context(), nil)
1833
+
tx, err := rp.db.BeginTx(r.Context(), nil)
1800
1834
if err != nil {
1801
1835
log.Println(err)
1802
-
s.pages.Notice(w, "repo", "Failed to save repository information.")
1836
+
rp.pages.Notice(w, "repo", "Failed to save repository information.")
1803
1837
return
1804
1838
}
1805
1839
defer func() {
1806
1840
tx.Rollback()
1807
-
err = s.enforcer.E.LoadPolicy()
1841
+
err = rp.enforcer.E.LoadPolicy()
1808
1842
if err != nil {
1809
1843
log.Println("failed to rollback policies")
1810
1844
}
···
1812
1846
1813
1847
resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName)
1814
1848
if err != nil {
1815
-
s.pages.Notice(w, "repo", "Failed to create repository on knot server.")
1849
+
rp.pages.Notice(w, "repo", "Failed to create repository on knot server.")
1816
1850
return
1817
1851
}
1818
1852
1819
1853
switch resp.StatusCode {
1820
1854
case http.StatusConflict:
1821
-
s.pages.Notice(w, "repo", "A repository with that name already exists.")
1855
+
rp.pages.Notice(w, "repo", "A repository with that name already exists.")
1822
1856
return
1823
1857
case http.StatusInternalServerError:
1824
-
s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
1858
+
rp.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")
1825
1859
case http.StatusNoContent:
1826
1860
// continue
1827
1861
}
1828
1862
1829
-
xrpcClient, err := s.oauth.AuthorizedClient(r)
1863
+
xrpcClient, err := rp.oauth.AuthorizedClient(r)
1830
1864
if err != nil {
1831
1865
log.Println("failed to get authorized client", err)
1832
-
s.pages.Notice(w, "repo", "Failed to create repository.")
1866
+
rp.pages.Notice(w, "repo", "Failed to create repository.")
1833
1867
return
1834
1868
}
1835
1869
···
1849
1883
})
1850
1884
if err != nil {
1851
1885
log.Printf("failed to create record: %s", err)
1852
-
s.pages.Notice(w, "repo", "Failed to announce repository creation.")
1886
+
rp.pages.Notice(w, "repo", "Failed to announce repository creation.")
1853
1887
return
1854
1888
}
1855
1889
log.Println("created repo record: ", atresp.Uri)
···
1858
1892
err = db.AddRepo(tx, repo)
1859
1893
if err != nil {
1860
1894
log.Println(err)
1861
-
s.pages.Notice(w, "repo", "Failed to save repository information.")
1895
+
rp.pages.Notice(w, "repo", "Failed to save repository information.")
1862
1896
return
1863
1897
}
1864
1898
1865
1899
// acls
1866
1900
p, _ := securejoin.SecureJoin(user.Did, forkName)
1867
-
err = s.enforcer.AddRepo(user.Did, knot, p)
1901
+
err = rp.enforcer.AddRepo(user.Did, knot, p)
1868
1902
if err != nil {
1869
1903
log.Println(err)
1870
-
s.pages.Notice(w, "repo", "Failed to set up repository permissions.")
1904
+
rp.pages.Notice(w, "repo", "Failed to set up repository permissions.")
1871
1905
return
1872
1906
}
1873
1907
···
1878
1912
return
1879
1913
}
1880
1914
1881
-
err = s.enforcer.E.SavePolicy()
1915
+
err = rp.enforcer.E.SavePolicy()
1882
1916
if err != nil {
1883
1917
log.Println("failed to update ACLs", err)
1884
1918
http.Error(w, err.Error(), http.StatusInternalServerError)
1885
1919
return
1886
1920
}
1887
1921
1888
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName))
1922
+
rp.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, forkName))
1889
1923
return
1890
1924
}
1891
1925
}
1892
1926
1893
-
func (s *State) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
1894
-
user := s.oauth.GetUser(r)
1895
-
f, err := s.repoResolver.Resolve(r)
1927
+
func (rp *Repo) RepoCompareNew(w http.ResponseWriter, r *http.Request) {
1928
+
user := rp.oauth.GetUser(r)
1929
+
f, err := rp.repoResolver.Resolve(r)
1896
1930
if err != nil {
1897
1931
log.Println("failed to get repo and knot", err)
1898
1932
return
1899
1933
}
1900
1934
1901
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
1935
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
1902
1936
if err != nil {
1903
1937
log.Printf("failed to create unsigned client for %s", f.Knot)
1904
-
s.pages.Error503(w)
1938
+
rp.pages.Error503(w)
1905
1939
return
1906
1940
}
1907
1941
1908
1942
result, err := us.Branches(f.OwnerDid(), f.RepoName)
1909
1943
if err != nil {
1910
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1944
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1911
1945
log.Println("failed to reach knotserver", err)
1912
1946
return
1913
1947
}
···
1938
1972
1939
1973
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
1940
1974
if err != nil {
1941
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1975
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1942
1976
log.Println("failed to reach knotserver", err)
1943
1977
return
1944
1978
}
1945
1979
1946
1980
repoinfo := f.RepoInfo(user)
1947
1981
1948
-
s.pages.RepoCompareNew(w, pages.RepoCompareNewParams{
1982
+
rp.pages.RepoCompareNew(w, pages.RepoCompareNewParams{
1949
1983
LoggedInUser: user,
1950
1984
RepoInfo: repoinfo,
1951
1985
Branches: branches,
···
1955
1989
})
1956
1990
}
1957
1991
1958
-
func (s *State) RepoCompare(w http.ResponseWriter, r *http.Request) {
1959
-
user := s.oauth.GetUser(r)
1960
-
f, err := s.repoResolver.Resolve(r)
1992
+
func (rp *Repo) RepoCompare(w http.ResponseWriter, r *http.Request) {
1993
+
user := rp.oauth.GetUser(r)
1994
+
f, err := rp.repoResolver.Resolve(r)
1961
1995
if err != nil {
1962
1996
log.Println("failed to get repo and knot", err)
1963
1997
return
···
1979
2013
1980
2014
if base == "" || head == "" {
1981
2015
log.Printf("invalid comparison")
1982
-
s.pages.Error404(w)
2016
+
rp.pages.Error404(w)
1983
2017
return
1984
2018
}
1985
2019
1986
-
us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev)
2020
+
us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev)
1987
2021
if err != nil {
1988
2022
log.Printf("failed to create unsigned client for %s", f.Knot)
1989
-
s.pages.Error503(w)
2023
+
rp.pages.Error503(w)
1990
2024
return
1991
2025
}
1992
2026
1993
2027
branches, err := us.Branches(f.OwnerDid(), f.RepoName)
1994
2028
if err != nil {
1995
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2029
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
1996
2030
log.Println("failed to reach knotserver", err)
1997
2031
return
1998
2032
}
1999
2033
2000
2034
tags, err := us.Tags(f.OwnerDid(), f.RepoName)
2001
2035
if err != nil {
2002
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2036
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2003
2037
log.Println("failed to reach knotserver", err)
2004
2038
return
2005
2039
}
2006
2040
2007
2041
formatPatch, err := us.Compare(f.OwnerDid(), f.RepoName, base, head)
2008
2042
if err != nil {
2009
-
s.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2043
+
rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.")
2010
2044
log.Println("failed to compare", err)
2011
2045
return
2012
2046
}
···
2014
2048
2015
2049
repoinfo := f.RepoInfo(user)
2016
2050
2017
-
s.pages.RepoCompare(w, pages.RepoCompareParams{
2051
+
rp.pages.RepoCompare(w, pages.RepoCompareParams{
2018
2052
LoggedInUser: user,
2019
2053
RepoInfo: repoinfo,
2020
2054
Branches: branches.Branches,
+4
-4
appview/state/repo_util.go
appview/repo/repo_util.go
+4
-4
appview/state/repo_util.go
appview/repo/repo_util.go
···
1
-
package state
1
+
package repo
2
2
3
3
import (
4
4
"context"
···
56
56
return
57
57
}
58
58
59
-
func EmailToDidOrHandle(s *State, emails []string) map[string]string {
60
-
emailToDid, err := db.GetEmailToDid(s.db, emails, true) // only get verified emails for mapping
59
+
func EmailToDidOrHandle(r *Repo, emails []string) map[string]string {
60
+
emailToDid, err := db.GetEmailToDid(r.db, emails, true) // only get verified emails for mapping
61
61
if err != nil {
62
62
log.Printf("error fetching dids for emails: %v", err)
63
63
return nil
···
67
67
for _, v := range emailToDid {
68
68
dids = append(dids, v)
69
69
}
70
-
resolvedIdents := s.idResolver.ResolveIdents(context.Background(), dids)
70
+
resolvedIdents := r.idResolver.ResolveIdents(context.Background(), dids)
71
71
72
72
didHandleMap := make(map[string]string)
73
73
for _, identity := range resolvedIdents {
+7
-85
appview/state/router.go
+7
-85
appview/state/router.go
···
9
9
"tangled.sh/tangled.sh/core/appview/middleware"
10
10
oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler"
11
11
"tangled.sh/tangled.sh/core/appview/pulls"
12
+
"tangled.sh/tangled.sh/core/appview/repo"
12
13
"tangled.sh/tangled.sh/core/appview/settings"
13
14
"tangled.sh/tangled.sh/core/appview/state/userutil"
14
15
)
···
69
70
r.With(mw.ResolveRepo()).Route("/{repo}", func(r chi.Router) {
70
71
r.Use(mw.GoImport())
71
72
72
-
r.Get("/", s.RepoIndex)
73
-
r.Get("/commits/{ref}", s.RepoLog)
74
-
r.Route("/tree/{ref}", func(r chi.Router) {
75
-
r.Get("/", s.RepoIndex)
76
-
r.Get("/*", s.RepoTree)
77
-
})
78
-
r.Get("/commit/{ref}", s.RepoCommit)
79
-
r.Get("/branches", s.RepoBranches)
80
-
r.Route("/tags", func(r chi.Router) {
81
-
r.Get("/", s.RepoTags)
82
-
r.Route("/{tag}", func(r chi.Router) {
83
-
r.Use(middleware.AuthMiddleware(s.oauth))
84
-
// require auth to download for now
85
-
r.Get("/download/{file}", s.DownloadArtifact)
86
-
87
-
// require repo:push to upload or delete artifacts
88
-
//
89
-
// additionally: only the uploader can truly delete an artifact
90
-
// (record+blob will live on their pds)
91
-
r.Group(func(r chi.Router) {
92
-
r.With(mw.RepoPermissionMiddleware("repo:push"))
93
-
r.Post("/upload", s.AttachArtifact)
94
-
r.Delete("/{file}", s.DeleteArtifact)
95
-
})
96
-
})
97
-
})
98
-
r.Get("/blob/{ref}/*", s.RepoBlob)
99
-
r.Get("/raw/{ref}/*", s.RepoBlobRaw)
100
-
101
-
r.Route("/issues", func(r chi.Router) {
102
-
r.With(middleware.Paginate).Get("/", s.RepoIssues)
103
-
r.Get("/{issue}", s.RepoSingleIssue)
104
-
105
-
r.Group(func(r chi.Router) {
106
-
r.Use(middleware.AuthMiddleware(s.oauth))
107
-
r.Get("/new", s.NewIssue)
108
-
r.Post("/new", s.NewIssue)
109
-
r.Post("/{issue}/comment", s.NewIssueComment)
110
-
r.Route("/{issue}/comment/{comment_id}/", func(r chi.Router) {
111
-
r.Get("/", s.IssueComment)
112
-
r.Delete("/", s.DeleteIssueComment)
113
-
r.Get("/edit", s.EditIssueComment)
114
-
r.Post("/edit", s.EditIssueComment)
115
-
})
116
-
r.Post("/{issue}/close", s.CloseIssue)
117
-
r.Post("/{issue}/reopen", s.ReopenIssue)
118
-
})
119
-
})
120
-
121
-
r.Route("/fork", func(r chi.Router) {
122
-
r.Use(middleware.AuthMiddleware(s.oauth))
123
-
r.Get("/", s.ForkRepo)
124
-
r.Post("/", s.ForkRepo)
125
-
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/sync", func(r chi.Router) {
126
-
r.Post("/", s.SyncRepoFork)
127
-
})
128
-
})
129
-
130
-
r.Route("/compare", func(r chi.Router) {
131
-
r.Get("/", s.RepoCompareNew) // start an new comparison
132
-
133
-
// we have to wildcard here since we want to support GitHub's compare syntax
134
-
// /compare/{ref1}...{ref2}
135
-
// for example:
136
-
// /compare/master...some/feature
137
-
// /compare/master...example.com:another/feature <- this is a fork
138
-
r.Get("/{base}/{head}", s.RepoCompare)
139
-
r.Get("/*", s.RepoCompare)
140
-
})
73
+
r.Mount("/", s.RepoRouter(mw))
141
74
142
75
r.Mount("/pulls", s.PullsRouter(mw))
143
76
···
146
79
r.Post("/git-upload-pack", s.UploadPack)
147
80
r.Post("/git-receive-pack", s.ReceivePack)
148
81
149
-
// settings routes, needs auth
150
-
r.Group(func(r chi.Router) {
151
-
r.Use(middleware.AuthMiddleware(s.oauth))
152
-
// repo description can only be edited by owner
153
-
r.With(mw.RepoPermissionMiddleware("repo:owner")).Route("/description", func(r chi.Router) {
154
-
r.Put("/", s.RepoDescription)
155
-
r.Get("/", s.RepoDescription)
156
-
r.Get("/edit", s.RepoDescriptionEdit)
157
-
})
158
-
r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) {
159
-
r.Get("/", s.RepoSettings)
160
-
r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", s.AddCollaborator)
161
-
r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", s.DeleteRepo)
162
-
r.Put("/branches/default", s.SetDefaultBranch)
163
-
})
164
-
})
165
82
})
166
83
})
167
84
···
266
183
pulls := pulls.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config)
267
184
return pulls.Router(mw)
268
185
}
186
+
187
+
func (s *State) RepoRouter(mw *middleware.Middleware) http.Handler {
188
+
repo := repo.New(s.oauth, s.repoResolver, s.pages, s.idResolver, s.db, s.config, s.posthog, s.enforcer)
189
+
return repo.Router(mw)
190
+
}