···387387 if err != nil {
388388 return fmt.Errorf("failed to update ACLs: %w", err)
389389 }
390390+391391+ l.Info("added spindle member")
390392 case models.CommitOperationDelete:
391393 rkey := e.Commit.RKey
392394···433435 if err = i.Enforcer.E.SavePolicy(); err != nil {
434436 return fmt.Errorf("failed to save ACLs: %w", err)
435437 }
438438+439439+ l.Info("removed spindle member")
436440 }
437441438442 return nil
+4-4
appview/spindles/spindles.go
···619619620620 if string(spindles[0].Owner) != user.Did {
621621 l.Error("unauthorized", "user", user.Did, "owner", spindles[0].Owner)
622622- s.Pages.Notice(w, noticeId, "Failed to add member, unauthorized attempt.")
622622+ s.Pages.Notice(w, noticeId, "Failed to remove member, unauthorized attempt.")
623623 return
624624 }
625625626626 member := r.FormValue("member")
627627 if member == "" {
628628 l.Error("empty member")
629629- s.Pages.Notice(w, noticeId, "Failed to add member, empty form.")
629629+ s.Pages.Notice(w, noticeId, "Failed to remove member, empty form.")
630630 return
631631 }
632632 l = l.With("member", member)
···634634 memberId, err := s.IdResolver.ResolveIdent(r.Context(), member)
635635 if err != nil {
636636 l.Error("failed to resolve member identity to handle", "err", err)
637637- s.Pages.Notice(w, noticeId, "Failed to add member, identity resolution failed.")
637637+ s.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
638638 return
639639 }
640640 if memberId.Handle.IsInvalidHandle() {
641641 l.Error("failed to resolve member identity to handle")
642642- s.Pages.Notice(w, noticeId, "Failed to add member, identity resolution failed.")
642642+ s.Pages.Notice(w, noticeId, "Failed to remove member, identity resolution failed.")
643643 return
644644 }
645645
+15
spindle/db/db.go
···4545 unique(owner, name)
4646 );
47474848+ create table if not exists spindle_members (
4949+ -- identifiers for the record
5050+ id integer primary key autoincrement,
5151+ did text not null,
5252+ rkey text not null,
5353+5454+ -- data
5555+ instance text not null,
5656+ subject text not null,
5757+ created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
5858+5959+ -- constraints
6060+ unique (did, instance, subject)
6161+ );
6262+4863 -- status event for a single workflow
4964 create table if not exists events (
5065 rkey text not null,
+59
spindle/db/member.go
···11+package db
22+33+import (
44+ "time"
55+66+ "github.com/bluesky-social/indigo/atproto/syntax"
77+)
88+99+type SpindleMember struct {
1010+ Id int
1111+ Did syntax.DID // owner of the record
1212+ Rkey string // rkey of the record
1313+ Instance string
1414+ Subject syntax.DID // the member being added
1515+ Created time.Time
1616+}
1717+1818+func AddSpindleMember(db *DB, member SpindleMember) error {
1919+ _, err := db.Exec(
2020+ `insert or ignore into spindle_members (did, rkey, instance, subject) values (?, ?, ?, ?)`,
2121+ member.Did,
2222+ member.Rkey,
2323+ member.Instance,
2424+ member.Subject,
2525+ )
2626+ return err
2727+}
2828+2929+func RemoveSpindleMember(db *DB, owner_did, rkey string) error {
3030+ _, err := db.Exec(
3131+ "delete from spindle_members where did = ? and rkey = ?",
3232+ owner_did,
3333+ rkey,
3434+ )
3535+ return err
3636+}
3737+3838+func GetSpindleMember(db *DB, did, rkey string) (*SpindleMember, error) {
3939+ query :=
4040+ `select id, did, rkey, instance, subject, created
4141+ from spindle_members
4242+ where did = ? and rkey = ?`
4343+4444+ var member SpindleMember
4545+ var createdAt string
4646+ err := db.QueryRow(query, did, rkey).Scan(
4747+ &member.Id,
4848+ &member.Did,
4949+ &member.Rkey,
5050+ &member.Instance,
5151+ &member.Subject,
5252+ &createdAt,
5353+ )
5454+ if err != nil {
5555+ return nil, err
5656+ }
5757+5858+ return &member, nil
5959+}
+39-4
spindle/ingester.go
···55 "encoding/json"
66 "errors"
77 "fmt"
88+ "time"
89910 "tangled.sh/tangled.sh/core/api/tangled"
1011 "tangled.sh/tangled.sh/core/eventconsumer"
1112 "tangled.sh/tangled.sh/core/idresolver"
1213 "tangled.sh/tangled.sh/core/rbac"
1414+ "tangled.sh/tangled.sh/core/spindle/db"
13151416 comatproto "github.com/bluesky-social/indigo/api/atproto"
1517 "github.com/bluesky-social/indigo/atproto/identity"
···5052}
51535254func (s *Spindle) ingestMember(_ context.Context, e *models.Event) error {
5555+ var err error
5356 did := e.Did
5454- var err error
5757+ rkey := e.Commit.RKey
55585659 l := s.l.With("component", "ingester", "record", tangled.SpindleMemberNSID)
5760···6669 }
67706871 domain := s.cfg.Server.Hostname
6969- if s.cfg.Server.Dev {
7070- domain = s.cfg.Server.ListenAddr
7171- }
7272 recordInstance := record.Instance
73737474 if recordInstance != domain {
···8282 return fmt.Errorf("failed to enforce permissions: %w", err)
8383 }
84848585+ if err := db.AddSpindleMember(s.db, db.SpindleMember{
8686+ Did: syntax.DID(did),
8787+ Rkey: rkey,
8888+ Instance: recordInstance,
8989+ Subject: syntax.DID(record.Subject),
9090+ Created: time.Now(),
9191+ }); err != nil {
9292+ l.Error("failed to add member", "error", err)
9393+ return fmt.Errorf("failed to add member: %w", err)
9494+ }
9595+8596 if err := s.e.AddSpindleMember(rbacDomain, record.Subject); err != nil {
8697 l.Error("failed to add member", "error", err)
8798 return fmt.Errorf("failed to add member: %w", err)
···95106 s.jc.AddDid(record.Subject)
9610797108 return nil
109109+110110+ case models.CommitOperationDelete:
111111+ record, err := db.GetSpindleMember(s.db, did, rkey)
112112+ if err != nil {
113113+ l.Error("failed to find member", "error", err)
114114+ return fmt.Errorf("failed to find member: %w", err)
115115+ }
116116+117117+ if err := db.RemoveSpindleMember(s.db, did, rkey); err != nil {
118118+ l.Error("failed to remove member", "error", err)
119119+ return fmt.Errorf("failed to remove member: %w", err)
120120+ }
121121+122122+ if err := s.e.RemoveSpindleMember(rbacDomain, record.Subject.String()); err != nil {
123123+ l.Error("failed to add member", "error", err)
124124+ return fmt.Errorf("failed to add member: %w", err)
125125+ }
126126+ l.Info("added member from firehose", "member", record.Subject)
127127+128128+ if err := s.db.RemoveDid(record.Subject.String()); err != nil {
129129+ l.Error("failed to add did", "error", err)
130130+ return fmt.Errorf("failed to add did: %w", err)
131131+ }
132132+ s.jc.RemoveDid(record.Subject.String())
9813399134 }
100135 return nil