···
233
-
func (s *State) EditPatch(w http.ResponseWriter, r *http.Request) {
234
-
user := s.auth.GetUser(r)
236
-
patch := r.FormValue("patch")
238
-
s.pages.Notice(w, "pull-error", "Patch is required.")
242
-
pull, ok := r.Context().Value("pull").(*db.Pull)
244
-
log.Println("failed to get pull")
245
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
249
-
if pull.OwnerDid != user.Did {
250
-
log.Println("failed to edit pull information")
251
-
s.pages.Notice(w, "pull-error", "Unauthorized")
255
-
f, err := fullyResolvedRepo(r)
257
-
log.Println("failed to get repo and knot", err)
258
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
262
-
// Start a transaction for database operations
263
-
tx, err := s.db.BeginTx(r.Context(), nil)
265
-
log.Println("failed to start transaction", err)
266
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
270
-
// Set up deferred rollback that will be overridden by commit if successful
271
-
defer tx.Rollback()
273
-
// Update patch in the database within transaction
274
-
err = db.EditPatch(tx, f.RepoAt, pull.PullId, patch)
276
-
log.Println("failed to update patch", err)
277
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
281
-
// Update the atproto record
282
-
client, _ := s.auth.AuthorizedClient(r)
283
-
pullAt := pull.PullAt
285
-
// Get the existing record first
286
-
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pullAt.RecordKey().String())
288
-
log.Println("failed to get existing pull record", err)
289
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
293
-
// Update the record
294
-
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
295
-
Collection: tangled.RepoPullNSID,
297
-
Rkey: pullAt.RecordKey().String(),
298
-
SwapRecord: ex.Cid,
299
-
Record: &lexutil.LexiconTypeDecoder{
300
-
Val: &tangled.RepoPull{
302
-
PullId: int64(pull.PullId),
303
-
TargetRepo: string(f.RepoAt),
304
-
TargetBranch: pull.TargetBranch,
311
-
log.Println("failed to update pull record in atproto", err)
312
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
316
-
// Commit the transaction now that both operations have succeeded
319
-
log.Println("failed to commit transaction", err)
320
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
324
-
targetBranch := pull.TargetBranch
326
-
// Perform merge check
327
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
329
-
log.Printf("no key found for domain %s: %s\n", f.Knot, err)
330
-
s.pages.Notice(w, "pull-success", "Patch updated successfully, but couldn't check mergeability.")
334
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
336
-
log.Printf("failed to create signed client for %s", f.Knot)
337
-
s.pages.Notice(w, "pull-success", "Patch updated successfully, but couldn't check mergeability.")
341
-
resp, err := ksClient.MergeCheck([]byte(patch), user.Did, f.RepoName, targetBranch)
343
-
log.Println("failed to check mergeability", err)
344
-
s.pages.Notice(w, "pull-success", "Patch updated successfully, but couldn't check mergeability.")
348
-
respBody, err := io.ReadAll(resp.Body)
350
-
log.Println("failed to read knotserver response body")
351
-
s.pages.Notice(w, "pull-success", "Patch updated successfully, but couldn't check mergeability.")
355
-
var mergeCheckResponse types.MergeCheckResponse
356
-
err = json.Unmarshal(respBody, &mergeCheckResponse)
358
-
log.Println("failed to unmarshal merge check response", err)
359
-
s.pages.Notice(w, "pull-success", "Patch updated successfully, but couldn't check mergeability.")
363
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.RepoName, pull.PullId))
367
-
func (s *State) NewPull(w http.ResponseWriter, r *http.Request) {
368
-
user := s.auth.GetUser(r)
369
-
f, err := fullyResolvedRepo(r)
371
-
log.Println("failed to get repo and knot", err)
376
-
case http.MethodGet:
377
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
379
-
log.Printf("failed to create unsigned client for %s", f.Knot)
380
-
s.pages.Error503(w)
384
-
resp, err := us.Branches(f.OwnerDid(), f.RepoName)
386
-
log.Println("failed to reach knotserver", err)
390
-
body, err := io.ReadAll(resp.Body)
392
-
log.Printf("Error reading response body: %v", err)
396
-
var result types.RepoBranchesResponse
397
-
err = json.Unmarshal(body, &result)
399
-
log.Println("failed to parse response:", err)
403
-
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
404
-
LoggedInUser: user,
405
-
RepoInfo: f.RepoInfo(s, user),
406
-
Branches: result.Branches,
408
-
case http.MethodPost:
409
-
title := r.FormValue("title")
410
-
body := r.FormValue("body")
411
-
targetBranch := r.FormValue("targetBranch")
412
-
patch := r.FormValue("patch")
414
-
if title == "" || body == "" || patch == "" || targetBranch == "" {
415
-
s.pages.Notice(w, "pull", "Title, body and patch diff are required.")
419
-
tx, err := s.db.BeginTx(r.Context(), nil)
421
-
log.Println("failed to start tx")
422
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
428
-
err = s.enforcer.E.LoadPolicy()
430
-
log.Println("failed to rollback policies")
434
-
err = db.NewPull(tx, &db.Pull{
437
-
TargetBranch: targetBranch,
439
-
OwnerDid: user.Did,
443
-
log.Println("failed to create pull request", err)
444
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
447
-
client, _ := s.auth.AuthorizedClient(r)
448
-
pullId, err := db.NextPullId(s.db, f.RepoAt)
450
-
log.Println("failed to get pull id", err)
451
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
455
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
456
-
Collection: tangled.RepoPullNSID,
459
-
Record: &lexutil.LexiconTypeDecoder{
460
-
Val: &tangled.RepoPull{
462
-
PullId: int64(pullId),
463
-
TargetRepo: string(f.RepoAt),
464
-
TargetBranch: targetBranch,
470
-
err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri)
472
-
log.Println("failed to get pull id", err)
473
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
477
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
482
-
func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) {
483
-
user := s.auth.GetUser(r)
484
-
f, err := fullyResolvedRepo(r)
486
-
log.Println("failed to get repo and knot", err)
490
-
pull, ok1 := r.Context().Value("pull").(*db.Pull)
491
-
comments, ok2 := r.Context().Value("pull_comments").([]db.PullComment)
493
-
log.Println("failed to get pull")
494
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
498
-
identsToResolve := make([]string, len(comments))
499
-
for i, comment := range comments {
500
-
identsToResolve[i] = comment.OwnerDid
502
-
identsToResolve = append(identsToResolve, pull.OwnerDid)
504
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
505
-
didHandleMap := make(map[string]string)
506
-
for _, identity := range resolvedIds {
507
-
if !identity.Handle.IsInvalidHandle() {
508
-
didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
510
-
didHandleMap[identity.DID.String()] = identity.DID.String()
514
-
var mergeCheckResponse types.MergeCheckResponse
516
-
// Only perform merge check if the pull request is not already merged
517
-
if pull.State != db.PullMerged {
518
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
520
-
log.Printf("failed to get registration key for %s", f.Knot)
521
-
s.pages.Notice(w, "pull", "Failed to load pull request. Try again later.")
525
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
527
-
resp, err := ksClient.MergeCheck([]byte(pull.Patch), pull.OwnerDid, f.RepoName, pull.TargetBranch)
529
-
log.Println("failed to check for mergeability:", err)
531
-
respBody, err := io.ReadAll(resp.Body)
533
-
log.Println("failed to read merge check response body")
535
-
err = json.Unmarshal(respBody, &mergeCheckResponse)
537
-
log.Println("failed to unmarshal merge check response", err)
542
-
log.Printf("failed to setup signed client for %s; ignoring...", f.Knot)
546
-
s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{
547
-
LoggedInUser: user,
548
-
RepoInfo: f.RepoInfo(s, user),
550
-
Comments: comments,
551
-
DidHandleMap: didHandleMap,
552
-
MergeCheck: mergeCheckResponse,
func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) {
f, err := fullyResolvedRepo(r)
···
s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
1380
-
func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) {
1381
-
user := s.auth.GetUser(r)
1382
-
params := r.URL.Query()
1384
-
state := db.PullOpen
1385
-
switch params.Get("state") {
1387
-
state = db.PullClosed
1389
-
state = db.PullMerged
1392
-
f, err := fullyResolvedRepo(r)
1394
-
log.Println("failed to get repo and knot", err)
1398
-
pulls, err := db.GetPulls(s.db, f.RepoAt, state)
1400
-
log.Println("failed to get pulls", err)
1401
-
s.pages.Notice(w, "pulls", "Failed to load pulls. Try again later.")
1405
-
identsToResolve := make([]string, len(pulls))
1406
-
for i, pull := range pulls {
1407
-
identsToResolve[i] = pull.OwnerDid
1409
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
1410
-
didHandleMap := make(map[string]string)
1411
-
for _, identity := range resolvedIds {
1412
-
if !identity.Handle.IsInvalidHandle() {
1413
-
didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
1415
-
didHandleMap[identity.DID.String()] = identity.DID.String()
1419
-
s.pages.RepoPulls(w, pages.RepoPullsParams{
1420
-
LoggedInUser: s.auth.GetUser(r),
1421
-
RepoInfo: f.RepoInfo(s, user),
1423
-
DidHandleMap: didHandleMap,
1424
-
FilteringBy: state,
1429
-
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
1430
-
user := s.auth.GetUser(r)
1431
-
f, err := fullyResolvedRepo(r)
1433
-
log.Println("failed to resolve repo:", err)
1434
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1438
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1440
-
log.Println("failed to get pull")
1441
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1445
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
1447
-
log.Printf("no registration key found for domain %s: %s\n", f.Knot, err)
1448
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1452
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
1454
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
1455
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1459
-
// Merge the pull request
1460
-
resp, err := ksClient.Merge([]byte(pull.Patch), user.Did, f.RepoName, pull.TargetBranch)
1462
-
log.Printf("failed to merge pull request: %s", err)
1463
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1467
-
if resp.StatusCode == http.StatusOK {
1468
-
err := db.MergePull(s.db, f.RepoAt, pull.PullId)
1470
-
log.Printf("failed to update pull request status in database: %s", err)
1471
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1474
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.RepoName, pull.PullId))
1476
-
log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode)
1477
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1481
-
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
1482
-
user := s.auth.GetUser(r)
1483
-
f, err := fullyResolvedRepo(r)
1485
-
log.Println("failed to get repo and knot", err)
1489
-
pullId := chi.URLParam(r, "pull")
1490
-
pullIdInt, err := strconv.Atoi(pullId)
1492
-
http.Error(w, "bad pull id", http.StatusBadRequest)
1493
-
log.Println("failed to parse pull id", err)
1498
-
case http.MethodPost:
1499
-
body := r.FormValue("body")
1501
-
s.pages.Notice(w, "pull", "Comment body is required")
1505
-
// Start a transaction
1506
-
tx, err := s.db.BeginTx(r.Context(), nil)
1508
-
log.Println("failed to start transaction", err)
1509
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1512
-
defer tx.Rollback() // Will be ignored if we commit
1514
-
commentId := rand.IntN(1000000)
1515
-
createdAt := time.Now().Format(time.RFC3339)
1516
-
commentIdInt64 := int64(commentId)
1517
-
ownerDid := user.Did
1519
-
pullAt, err := db.GetPullAt(s.db, f.RepoAt, pullIdInt)
1521
-
log.Println("failed to get pull at", err)
1522
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1526
-
atUri := f.RepoAt.String()
1527
-
client, _ := s.auth.AuthorizedClient(r)
1528
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1529
-
Collection: tangled.RepoPullCommentNSID,
1532
-
Record: &lexutil.LexiconTypeDecoder{
1533
-
Val: &tangled.RepoPullComment{
1536
-
CommentId: &commentIdInt64,
1539
-
CreatedAt: &createdAt,
1544
-
log.Println("failed to create pull comment", err)
1545
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1549
-
// Create the pull comment in the database with the commentAt field
1550
-
err = db.NewPullComment(tx, &db.PullComment{
1551
-
OwnerDid: user.Did,
1552
-
RepoAt: f.RepoAt.String(),
1553
-
CommentId: commentId,
1554
-
PullId: pullIdInt,
1556
-
CommentAt: atResp.Uri,
1559
-
log.Println("failed to create pull comment", err)
1560
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1564
-
// Commit the transaction
1565
-
if err = tx.Commit(); err != nil {
1566
-
log.Println("failed to commit transaction", err)
1567
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1571
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pullIdInt, commentId))
1576
-
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
1577
-
user := s.auth.GetUser(r)
1579
-
f, err := fullyResolvedRepo(r)
1581
-
log.Println("malformed middleware")
1585
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1587
-
log.Println("failed to get pull")
1588
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1592
-
// auth filter: only owner or collaborators can close
1593
-
roles := RolesInRepo(s, user, f)
1594
-
isCollaborator := roles.IsCollaborator()
1595
-
isPullAuthor := user.Did == pull.OwnerDid
1596
-
isCloseAllowed := isCollaborator || isPullAuthor
1597
-
if !isCloseAllowed {
1598
-
log.Println("failed to close pull")
1599
-
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
1603
-
// Start a transaction
1604
-
tx, err := s.db.BeginTx(r.Context(), nil)
1606
-
log.Println("failed to start transaction", err)
1607
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1611
-
// Close the pull in the database
1612
-
err = db.ClosePull(tx, f.RepoAt, pull.PullId)
1614
-
log.Println("failed to close pull", err)
1615
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1619
-
// Commit the transaction
1620
-
if err = tx.Commit(); err != nil {
1621
-
log.Println("failed to commit transaction", err)
1622
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1626
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1630
-
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
1631
-
user := s.auth.GetUser(r)
1633
-
f, err := fullyResolvedRepo(r)
1635
-
log.Println("failed to resolve repo", err)
1636
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1640
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1642
-
log.Println("failed to get pull")
1643
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1647
-
// auth filter: only owner or collaborators can close
1648
-
roles := RolesInRepo(s, user, f)
1649
-
isCollaborator := roles.IsCollaborator()
1650
-
isPullAuthor := user.Did == pull.OwnerDid
1651
-
isCloseAllowed := isCollaborator || isPullAuthor
1652
-
if !isCloseAllowed {
1653
-
log.Println("failed to close pull")
1654
-
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
1658
-
// Start a transaction
1659
-
tx, err := s.db.BeginTx(r.Context(), nil)
1661
-
log.Println("failed to start transaction", err)
1662
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1666
-
// Reopen the pull in the database
1667
-
err = db.ReopenPull(tx, f.RepoAt, pull.PullId)
1669
-
log.Println("failed to reopen pull", err)
1670
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1674
-
// Commit the transaction
1675
-
if err = tx.Commit(); err != nil {
1676
-
log.Println("failed to commit transaction", err)
1677
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1681
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {