···3535 did text primary key3636 );37373838+ create table if not exists repos (3939+ id integer primary key autoincrement,4040+ knot text not null,4141+ owner text not null,4242+ name text not null,4343+ addedAt text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),4444+4545+ unique(owner, name)4646+ );4747+3848 -- status event for a single workflow3949 create table if not exists events (4050 rkey text not null,
+28
spindle/db/repos.go
···11+package db22+33+func (d *DB) AddRepo(knot, owner, name string) error {44+ _, err := d.Exec(`insert or ignore into repos (knot, owner, name) values (?, ?, ?)`, knot, owner, name)55+ return err66+}77+88+func (d *DB) Knots() ([]string, error) {99+ rows, err := d.Query(`select knot from repos`)1010+ if err != nil {1111+ return nil, err1212+ }1313+1414+ var knots []string1515+ for rows.Next() {1616+ var knot string1717+ if err := rows.Scan(&knot); err != nil {1818+ return nil, err1919+ }2020+ knots = append(knots, knot)2121+ }2222+2323+ if err = rows.Err(); err != nil {2424+ return nil, err2525+ }2626+2727+ return knots, nil2828+}
+52-2
spindle/ingester.go
···55 "encoding/json"66 "fmt"7788- "github.com/bluesky-social/jetstream/pkg/models"98 "tangled.sh/tangled.sh/core/api/tangled"99+ "tangled.sh/tangled.sh/core/knotclient"1010+1111+ "github.com/bluesky-social/jetstream/pkg/models"1012)11131214type Ingester func(ctx context.Context, e *models.Event) error···3129 switch e.Commit.Collection {3230 case tangled.SpindleMemberNSID:3331 s.ingestMember(ctx, e)3232+ case tangled.RepoNSID:3333+ s.ingestRepo(ctx, e)3434 }35353636 return err···7268 return fmt.Errorf("failed to enforce permissions: %w", err)7369 }74707575- if err := s.e.AddMember(rbacDomain, record.Subject); err != nil {7171+ if err := s.e.AddKnotMember(rbacDomain, record.Subject); err != nil {7672 l.Error("failed to add member", "error", err)7773 return fmt.Errorf("failed to add member: %w", err)7874 }···8379 return fmt.Errorf("failed to add did: %w", err)8480 }8581 s.jc.AddDid(did)8282+8383+ return nil8484+8585+ }8686+ return nil8787+}8888+8989+func (s *Spindle) ingestRepo(_ context.Context, e *models.Event) error {9090+ var err error9191+9292+ l := s.l.With("component", "ingester", "record", tangled.RepoNSID)9393+9494+ switch e.Commit.Operation {9595+ case models.CommitOperationCreate, models.CommitOperationUpdate:9696+ raw := e.Commit.Record9797+ record := tangled.Repo{}9898+ err = json.Unmarshal(raw, &record)9999+ if err != nil {100100+ l.Error("invalid record", "error", err)101101+ return err102102+ }103103+104104+ domain := s.cfg.Server.Hostname105105+ if s.cfg.Server.Dev {106106+ domain = s.cfg.Server.ListenAddr107107+ }108108+109109+ // no spindle configured for this repo110110+ if record.Spindle == nil {111111+ return nil112112+ }113113+114114+ // this repo did not want this spindle115115+ if *record.Spindle != domain {116116+ return nil117117+ }118118+119119+ // add this repo to the watch list120120+ if err := s.db.AddRepo(record.Knot, record.Owner, record.Name); err != nil {121121+ l.Error("failed to add repo", "error", err)122122+ return fmt.Errorf("failed to add repo: %w", err)123123+ }124124+125125+ // add this knot to the event consumer126126+ src := knotclient.NewEventSource(record.Knot)127127+ s.ks.AddSource(context.Background(), src)8612887129 return nil88130