A vibe coded tangled fork which supports pijul.
at f4a09133a636451c495fa96c98b99379b7692497 148 lines 3.9 kB view raw
1package state 2 3import ( 4 "log" 5 "net/http" 6 "time" 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 lexutil "github.com/bluesky-social/indigo/lex/util" 11 "tangled.org/core/api/tangled" 12 "tangled.org/core/appview/db" 13 "tangled.org/core/appview/models" 14 "tangled.org/core/appview/pages" 15 "tangled.org/core/tid" 16) 17 18func (s *State) Follow(w http.ResponseWriter, r *http.Request) { 19 currentUser := s.oauth.GetMultiAccountUser(r) 20 21 subject := r.URL.Query().Get("subject") 22 if subject == "" { 23 log.Println("invalid form") 24 return 25 } 26 27 subjectIdent, err := s.idResolver.ResolveIdent(r.Context(), subject) 28 if err != nil { 29 log.Println("failed to follow, invalid did") 30 return 31 } 32 33 if currentUser.Active.Did == subjectIdent.DID.String() { 34 log.Println("cant follow or unfollow yourself") 35 return 36 } 37 38 client, err := s.oauth.AuthorizedClient(r) 39 if err != nil { 40 log.Println("failed to authorize client") 41 return 42 } 43 44 switch r.Method { 45 case http.MethodPost: 46 createdAt := time.Now().Format(time.RFC3339) 47 rkey := tid.TID() 48 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 49 Collection: tangled.GraphFollowNSID, 50 Repo: currentUser.Active.Did, 51 Rkey: rkey, 52 Record: &lexutil.LexiconTypeDecoder{ 53 Val: &tangled.GraphFollow{ 54 Subject: subjectIdent.DID.String(), 55 CreatedAt: createdAt, 56 }}, 57 }) 58 if err != nil { 59 log.Println("failed to create atproto record", err) 60 return 61 } 62 63 log.Println("created atproto record: ", resp.Uri) 64 65 follow := &models.Follow{ 66 UserDid: currentUser.Active.Did, 67 SubjectDid: subjectIdent.DID.String(), 68 Rkey: rkey, 69 } 70 71 err = db.AddFollow(s.db, follow) 72 if err != nil { 73 log.Println("failed to follow", err) 74 return 75 } 76 77 s.notifier.NewFollow(r.Context(), follow) 78 79 followStats, err := db.GetFollowerFollowingCount(s.db, subjectIdent.DID.String()) 80 if err != nil { 81 log.Println("failed to get follow stats", err) 82 } 83 84 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 85 UserDid: subjectIdent.DID.String(), 86 FollowStatus: models.IsFollowing, 87 FollowersCount: followStats.Followers, 88 }) 89 90 return 91 case http.MethodDelete: 92 tx, err := s.db.BeginTx(r.Context(), nil) 93 if err != nil { 94 s.logger.Error("failed to start transaction", "err", err) 95 } 96 defer tx.Rollback() 97 98 follows, err := db.DeleteFollow(tx, syntax.DID(currentUser.Active.Did), subjectIdent.DID) 99 if err != nil { 100 s.logger.Error("failed to delete follows from db", "err", err) 101 return 102 } 103 104 var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem 105 for _, followAt := range follows { 106 writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{ 107 RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{ 108 Collection: tangled.GraphFollowNSID, 109 Rkey: followAt.RecordKey().String(), 110 }, 111 }) 112 } 113 _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{ 114 Repo: currentUser.Active.Did, 115 Writes: writes, 116 }) 117 if err != nil { 118 s.logger.Error("failed to delete follows from PDS", "err", err) 119 return 120 } 121 122 if err := tx.Commit(); err != nil { 123 s.logger.Error("failed to commit transaction", "err", err) 124 // DB op failed but record is created in PDS. Ingester will backfill the missed operation 125 } 126 127 s.notifier.DeleteFollow(r.Context(), &models.Follow{ 128 UserDid: currentUser.Active.Did, 129 SubjectDid: subjectIdent.DID.String(), 130 // Rkey 131 // FollowedAt 132 }) 133 134 followStats, err := db.GetFollowerFollowingCount(s.db, subjectIdent.DID.String()) 135 if err != nil { 136 log.Println("failed to get follow stats", err) 137 } 138 139 s.pages.FollowFragment(w, pages.FollowFragmentParams{ 140 UserDid: subjectIdent.DID.String(), 141 FollowStatus: models.IsNotFollowing, 142 FollowersCount: followStats.Followers, 143 }) 144 145 return 146 } 147 148}