···
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))
1396
-
func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) {
1397
-
user := s.auth.GetUser(r)
1398
-
params := r.URL.Query()
1400
-
state := db.PullOpen
1401
-
switch params.Get("state") {
1403
-
state = db.PullClosed
1405
-
state = db.PullMerged
1408
-
f, err := fullyResolvedRepo(r)
1410
-
log.Println("failed to get repo and knot", err)
1414
-
pulls, err := db.GetPulls(s.db, f.RepoAt, state)
1416
-
log.Println("failed to get pulls", err)
1417
-
s.pages.Notice(w, "pulls", "Failed to load pulls. Try again later.")
1421
-
identsToResolve := make([]string, len(pulls))
1422
-
for i, pull := range pulls {
1423
-
identsToResolve[i] = pull.OwnerDid
1425
-
resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve)
1426
-
didHandleMap := make(map[string]string)
1427
-
for _, identity := range resolvedIds {
1428
-
if !identity.Handle.IsInvalidHandle() {
1429
-
didHandleMap[identity.DID.String()] = fmt.Sprintf("@%s", identity.Handle.String())
1431
-
didHandleMap[identity.DID.String()] = identity.DID.String()
1435
-
s.pages.RepoPulls(w, pages.RepoPullsParams{
1436
-
LoggedInUser: s.auth.GetUser(r),
1437
-
RepoInfo: f.RepoInfo(s, user),
1439
-
DidHandleMap: didHandleMap,
1440
-
FilteringBy: state,
1445
-
func (s *State) MergePull(w http.ResponseWriter, r *http.Request) {
1446
-
user := s.auth.GetUser(r)
1447
-
f, err := fullyResolvedRepo(r)
1449
-
log.Println("failed to resolve repo:", err)
1450
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1454
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1456
-
log.Println("failed to get pull")
1457
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1461
-
secret, err := db.GetRegistrationKey(s.db, f.Knot)
1463
-
log.Printf("no registration key found for domain %s: %s\n", f.Knot, err)
1464
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1468
-
ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev)
1470
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
1471
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1475
-
// Merge the pull request
1476
-
resp, err := ksClient.Merge([]byte(pull.Patch), user.Did, f.RepoName, pull.TargetBranch)
1478
-
log.Printf("failed to merge pull request: %s", err)
1479
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1483
-
if resp.StatusCode == http.StatusOK {
1484
-
err := db.MergePull(s.db, f.RepoAt, pull.PullId)
1486
-
log.Printf("failed to update pull request status in database: %s", err)
1487
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1490
-
s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.RepoName, pull.PullId))
1492
-
log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode)
1493
-
s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.")
1497
-
func (s *State) PullComment(w http.ResponseWriter, r *http.Request) {
1498
-
user := s.auth.GetUser(r)
1499
-
f, err := fullyResolvedRepo(r)
1501
-
log.Println("failed to get repo and knot", err)
1505
-
pullId := chi.URLParam(r, "pull")
1506
-
pullIdInt, err := strconv.Atoi(pullId)
1508
-
http.Error(w, "bad pull id", http.StatusBadRequest)
1509
-
log.Println("failed to parse pull id", err)
1514
-
case http.MethodPost:
1515
-
body := r.FormValue("body")
1517
-
s.pages.Notice(w, "pull", "Comment body is required")
1521
-
// Start a transaction
1522
-
tx, err := s.db.BeginTx(r.Context(), nil)
1524
-
log.Println("failed to start transaction", err)
1525
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1528
-
defer tx.Rollback() // Will be ignored if we commit
1530
-
commentId := rand.IntN(1000000)
1531
-
createdAt := time.Now().Format(time.RFC3339)
1532
-
commentIdInt64 := int64(commentId)
1533
-
ownerDid := user.Did
1535
-
pullAt, err := db.GetPullAt(s.db, f.RepoAt, pullIdInt)
1537
-
log.Println("failed to get pull at", err)
1538
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1542
-
atUri := f.RepoAt.String()
1543
-
client, _ := s.auth.AuthorizedClient(r)
1544
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
1545
-
Collection: tangled.RepoPullCommentNSID,
1548
-
Record: &lexutil.LexiconTypeDecoder{
1549
-
Val: &tangled.RepoPullComment{
1552
-
CommentId: &commentIdInt64,
1555
-
CreatedAt: &createdAt,
1560
-
log.Println("failed to create pull comment", err)
1561
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1565
-
// Create the pull comment in the database with the commentAt field
1566
-
err = db.NewPullComment(tx, &db.PullComment{
1567
-
OwnerDid: user.Did,
1568
-
RepoAt: f.RepoAt.String(),
1569
-
CommentId: commentId,
1570
-
PullId: pullIdInt,
1572
-
CommentAt: atResp.Uri,
1575
-
log.Println("failed to create pull comment", err)
1576
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1580
-
// Commit the transaction
1581
-
if err = tx.Commit(); err != nil {
1582
-
log.Println("failed to commit transaction", err)
1583
-
s.pages.Notice(w, "pull-comment", "Failed to create comment.")
1587
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pullIdInt, commentId))
1592
-
func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) {
1593
-
user := s.auth.GetUser(r)
1595
-
f, err := fullyResolvedRepo(r)
1597
-
log.Println("malformed middleware")
1601
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1603
-
log.Println("failed to get pull")
1604
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1608
-
// auth filter: only owner or collaborators can close
1609
-
roles := RolesInRepo(s, user, f)
1610
-
isCollaborator := roles.IsCollaborator()
1611
-
isPullAuthor := user.Did == pull.OwnerDid
1612
-
isCloseAllowed := isCollaborator || isPullAuthor
1613
-
if !isCloseAllowed {
1614
-
log.Println("failed to close pull")
1615
-
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
1619
-
// Start a transaction
1620
-
tx, err := s.db.BeginTx(r.Context(), nil)
1622
-
log.Println("failed to start transaction", err)
1623
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1627
-
// Close the pull in the database
1628
-
err = db.ClosePull(tx, f.RepoAt, pull.PullId)
1630
-
log.Println("failed to close pull", err)
1631
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1635
-
// Commit the transaction
1636
-
if err = tx.Commit(); err != nil {
1637
-
log.Println("failed to commit transaction", err)
1638
-
s.pages.Notice(w, "pull-close", "Failed to close pull.")
1642
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
1646
-
func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) {
1647
-
user := s.auth.GetUser(r)
1649
-
f, err := fullyResolvedRepo(r)
1651
-
log.Println("failed to resolve repo", err)
1652
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1656
-
pull, ok := r.Context().Value("pull").(*db.Pull)
1658
-
log.Println("failed to get pull")
1659
-
s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.")
1663
-
// auth filter: only owner or collaborators can close
1664
-
roles := RolesInRepo(s, user, f)
1665
-
isCollaborator := roles.IsCollaborator()
1666
-
isPullAuthor := user.Did == pull.OwnerDid
1667
-
isCloseAllowed := isCollaborator || isPullAuthor
1668
-
if !isCloseAllowed {
1669
-
log.Println("failed to close pull")
1670
-
s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.")
1674
-
// Start a transaction
1675
-
tx, err := s.db.BeginTx(r.Context(), nil)
1677
-
log.Println("failed to start transaction", err)
1678
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1682
-
// Reopen the pull in the database
1683
-
err = db.ReopenPull(tx, f.RepoAt, pull.PullId)
1685
-
log.Println("failed to reopen pull", err)
1686
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1690
-
// Commit the transaction
1691
-
if err = tx.Commit(); err != nil {
1692
-
log.Println("failed to commit transaction", err)
1693
-
s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.")
1697
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pull.PullId))
func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {