···
43
43
create table if not exists follows (
44
44
user_did text not null,
45
45
subject_did text not null,
46
46
+
at_uri text not null,
46
47
followed_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
47
48
primary key (user_did, subject_did),
48
49
check (user_did <> subject_did)
···
1
1
package db
2
2
3
3
-
func (d *DB) AddFollow(userDid, subjectDid string) error {
4
4
-
query := `insert into follows (user_did, subject_did) values (?, ?)`
5
5
-
_, err := d.db.Exec(query, userDid, subjectDid)
3
3
+
import (
4
4
+
"log"
5
5
+
"time"
6
6
+
)
7
7
+
8
8
+
type Follow struct {
9
9
+
UserDid string
10
10
+
SubjectDid string
11
11
+
FollowedAt *time.Time
12
12
+
AtUri string
13
13
+
}
14
14
+
15
15
+
func (d *DB) AddFollow(userDid, subjectDid, atUri string) error {
16
16
+
query := `insert into follows (user_did, subject_did, at_uri) values (?, ?, ?)`
17
17
+
_, err := d.db.Exec(query, userDid, subjectDid, atUri)
18
18
+
return err
19
19
+
}
20
20
+
21
21
+
// Get a follow record
22
22
+
func (d *DB) GetFollow(userDid, subjectDid string) (*Follow, error) {
23
23
+
query := `select user_did, subject_did, followed_at, at_uri from follows where user_did = ? and subject_did = ?`
24
24
+
row := d.db.QueryRow(query, userDid, subjectDid)
25
25
+
26
26
+
var follow Follow
27
27
+
var followedAt string
28
28
+
err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.AtUri)
29
29
+
if err != nil {
30
30
+
return nil, err
31
31
+
}
32
32
+
33
33
+
followedAtTime, err := time.Parse(time.RFC3339, followedAt)
34
34
+
if err != nil {
35
35
+
log.Println("unable to determine followed at time")
36
36
+
follow.FollowedAt = nil
37
37
+
} else {
38
38
+
follow.FollowedAt = &followedAtTime
39
39
+
}
40
40
+
41
41
+
return &follow, nil
42
42
+
}
43
43
+
44
44
+
// Get a follow record
45
45
+
func (d *DB) DeleteFollow(userDid, subjectDid string) error {
46
46
+
_, err := d.db.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid)
6
47
return err
7
48
}
···
531
531
}
532
532
533
533
func (s *State) Follow(w http.ResponseWriter, r *http.Request) {
534
534
-
subject := r.FormValue("subject")
534
534
+
currentUser := s.auth.GetUser(r)
535
535
536
536
+
subject := r.URL.Query().Get("subject")
536
537
if subject == "" {
537
538
log.Println("invalid form")
538
539
return
539
540
}
540
541
541
542
subjectIdent, err := s.resolver.ResolveIdent(r.Context(), subject)
542
542
-
currentUser := s.auth.GetUser(r)
543
543
+
if err != nil {
544
544
+
log.Println("failed to follow, invalid did")
545
545
+
}
546
546
+
547
547
+
if currentUser.Did == subjectIdent.DID.String() {
548
548
+
log.Println("cant follow or unfollow yourself")
549
549
+
return
550
550
+
}
543
551
544
552
client, _ := s.auth.AuthorizedClient(r)
545
545
-
createdAt := time.Now().Format(time.RFC3339)
546
546
-
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
547
547
-
Collection: tangled.GraphFollowNSID,
548
548
-
Repo: currentUser.Did,
549
549
-
Rkey: s.TID(),
550
550
-
Record: &lexutil.LexiconTypeDecoder{
551
551
-
Val: &tangled.GraphFollow{
552
552
-
Subject: subjectIdent.DID.String(),
553
553
-
CreatedAt: createdAt,
554
554
-
}},
555
555
-
})
553
553
+
554
554
+
switch r.Method {
555
555
+
case http.MethodPost:
556
556
+
createdAt := time.Now().Format(time.RFC3339)
557
557
+
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
558
558
+
Collection: tangled.GraphFollowNSID,
559
559
+
Repo: currentUser.Did,
560
560
+
Rkey: s.TID(),
561
561
+
Record: &lexutil.LexiconTypeDecoder{
562
562
+
Val: &tangled.GraphFollow{
563
563
+
Subject: subjectIdent.DID.String(),
564
564
+
CreatedAt: createdAt,
565
565
+
}},
566
566
+
})
567
567
+
if err != nil {
568
568
+
log.Println("failed to create atproto record", err)
569
569
+
return
570
570
+
}
571
571
+
572
572
+
err = s.db.AddFollow(currentUser.Did, subjectIdent.DID.String(), resp.Uri)
573
573
+
if err != nil {
574
574
+
log.Println("failed to follow", err)
575
575
+
return
576
576
+
}
577
577
+
578
578
+
log.Println("created atproto record: ", resp.Uri)
579
579
+
580
580
+
return
581
581
+
case http.MethodDelete:
582
582
+
// find the record in the db
583
583
+
584
584
+
follow, err := s.db.GetFollow(currentUser.Did, subjectIdent.DID.String())
585
585
+
if err != nil {
586
586
+
log.Println("failed to get follow relationship")
587
587
+
return
588
588
+
}
589
589
+
590
590
+
existingRecordUri, _ := syntax.ParseATURI(follow.AtUri)
591
591
+
592
592
+
resp, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{
593
593
+
Collection: tangled.GraphFollowNSID,
594
594
+
Repo: currentUser.Did,
595
595
+
Rkey: existingRecordUri.RecordKey().String(),
596
596
+
})
597
597
+
598
598
+
log.Println(resp.Commit.Cid)
599
599
+
600
600
+
if err != nil {
601
601
+
log.Println("failed to unfollow")
602
602
+
return
603
603
+
}
604
604
+
605
605
+
err = s.db.DeleteFollow(currentUser.Did, subjectIdent.DID.String())
606
606
+
if err != nil {
607
607
+
log.Println("failed to delete follow from DB")
608
608
+
// this is not an issue, the firehose event might have already done this
609
609
+
}
556
610
557
557
-
err = s.db.AddFollow(currentUser.Did, subjectIdent.DID.String())
558
558
-
if err != nil {
559
559
-
log.Println("failed to follow", err)
611
611
+
w.WriteHeader(http.StatusNoContent)
560
612
return
561
613
}
562
614
563
563
-
log.Println("created atproto record: ", resp.Uri)
564
564
-
565
565
-
return
566
615
}
567
616
568
617
func (s *State) Router() http.Handler {
···
655
704
// r.Post("/import", s.ImportRepo)
656
705
})
657
706
658
658
-
r.With(AuthMiddleware(s)).Put("/follow", s.Follow)
707
707
+
r.With(AuthMiddleware(s)).Route("/follow", func(r chi.Router) {
708
708
+
r.Post("/", s.Follow)
709
709
+
r.Delete("/", s.Follow)
710
710
+
})
659
711
660
712
r.Route("/settings", func(r chi.Router) {
661
713
r.Use(AuthMiddleware(s))