···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.RKey392394···435433 if err = i.Enforcer.E.SavePolicy(); err != nil {436434 return fmt.Errorf("failed to save ACLs: %w", err)437435 }436436+437437+ l.Info("removed spindle member")438438 }439439440440 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 return624624 }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 return631631 }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 return639639 }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 return644644 }645645
+15
spindle/db/db.go
···4545 unique(owner, name)4646 );47474848+ create table if not exists spindle_members (4949+ -- identifiers for the record5050+ id integer primary key autoincrement,5151+ did text not null,5252+ rkey text not null,5353+5454+ -- data5555+ 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+ -- constraints6060+ unique (did, instance, subject)6161+ );6262+4863 -- status event for a single workflow4964 create table if not exists events (5065 rkey text not null,
+59
spindle/db/member.go
···11+package db22+33+import (44+ "time"55+66+ "github.com/bluesky-social/indigo/atproto/syntax"77+)88+99+type SpindleMember struct {1010+ Id int1111+ Did syntax.DID // owner of the record1212+ Rkey string // rkey of the record1313+ Instance string1414+ Subject syntax.DID // the member being added1515+ Created time.Time1616+}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 err2727+}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 err3636+}3737+3838+func GetSpindleMember(db *DB, did, rkey string) (*SpindleMember, error) {3939+ query :=4040+ `select id, did, rkey, instance, subject, created4141+ from spindle_members4242+ where did = ? and rkey = ?`4343+4444+ var member SpindleMember4545+ var createdAt string4646+ 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, err5656+ }5757+5858+ return &member, nil5959+}
+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"···5250}53515452func (s *Spindle) ingestMember(_ context.Context, e *models.Event) error {5555- did := e.Did5653 var err error5454+ did := e.Did5555+ rkey := e.Commit.RKey57565857 l := s.l.With("component", "ingester", "record", tangled.SpindleMemberNSID)5958···6966 }70677168 domain := s.cfg.Server.Hostname7272- if s.cfg.Server.Dev {7373- domain = s.cfg.Server.ListenAddr7474- }7569 recordInstance := record.Instance76707771 if recordInstance != domain {···8080 if err != nil || !ok {8181 l.Error("failed to add member", "did", did, "error", err)8282 return fmt.Errorf("failed to enforce permissions: %w", err)8383+ }8484+8585+ 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)8394 }84958596 if err := s.e.AddSpindleMember(rbacDomain, record.Subject); err != nil {···10695 s.jc.AddDid(record.Subject)1079610897 return nil9898+9999+ case models.CommitOperationDelete:100100+ record, err := db.GetSpindleMember(s.db, did, rkey)101101+ if err != nil {102102+ l.Error("failed to find member", "error", err)103103+ return fmt.Errorf("failed to find member: %w", err)104104+ }105105+106106+ if err := db.RemoveSpindleMember(s.db, did, rkey); err != nil {107107+ l.Error("failed to remove member", "error", err)108108+ return fmt.Errorf("failed to remove member: %w", err)109109+ }110110+111111+ if err := s.e.RemoveSpindleMember(rbacDomain, record.Subject.String()); err != nil {112112+ l.Error("failed to add member", "error", err)113113+ return fmt.Errorf("failed to add member: %w", err)114114+ }115115+ l.Info("added member from firehose", "member", record.Subject)116116+117117+ if err := s.db.RemoveDid(record.Subject.String()); err != nil {118118+ l.Error("failed to add did", "error", err)119119+ return fmt.Errorf("failed to add did: %w", err)120120+ }121121+ s.jc.RemoveDid(record.Subject.String())109122110123 }111124 return nil