Monorepo for Tangled tangled.org
5

Configure Feed

Select the types of activity you want to include in your feed.

at icy/yrolzt 3.3 kB View raw
1package keys 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/atproto/identity" 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 12 "tangled.org/core/api/tangled" 13 "tangled.org/core/knotserver/db" 14 "tangled.org/core/log" 15) 16 17const ( 18 publicKeyPageSize = 100 19 maxPublicKeyPages = 20 20) 21 22func FetchAndStore(ctx context.Context, dir identity.Directory, store *db.DB, did syntax.DID) error { 23 l := log.FromContext(ctx) 24 25 id, err := dir.LookupDID(ctx, did) 26 if err != nil { 27 return fmt.Errorf("lookup did to fetch keys: %w", err) 28 } 29 30 serviceEndpoint, ok := id.Services["atproto_pds"] 31 if !ok { 32 l.Warn("did identity did not contain atproto_pds service while adding their keys", "did", did) 33 return nil 34 } 35 36 xrpcc := indigoxrpc.Client{Host: serviceEndpoint.URL} 37 records, err := listAllPublicKeys(ctx, l, &xrpcc, did, "", maxPublicKeyPages) 38 if err != nil { 39 return fmt.Errorf("fetching public keys for did: %w", err) 40 } 41 42 keys := collectPublicKeys(l, did, records) 43 if len(keys) == 0 { 44 l.Warn("no public keys fetched, skipping replace so existing keys are not wiped by a transient empty response", "did", did) 45 return nil 46 } 47 48 if err := store.ReplacePublicKeys(did, keys); err != nil { 49 return fmt.Errorf("replacing public keys in db: %w", err) 50 } 51 return nil 52} 53 54func listAllPublicKeys(ctx context.Context, l *slog.Logger, xrpcc *indigoxrpc.Client, did syntax.DID, cursor string, pagesLeft int) ([]*comatproto.RepoListRecords_Record, error) { 55 if pagesLeft <= 0 { 56 l.Warn("public key pagination hit page cap, remaining keys ignored", "did", did, "cap", maxPublicKeyPages) 57 return nil, nil 58 } 59 60 resp, err := comatproto.RepoListRecords(ctx, xrpcc, tangled.PublicKeyNSID, cursor, publicKeyPageSize, did.String(), false) 61 if err != nil { 62 return nil, err 63 } 64 65 if resp.Cursor == nil || *resp.Cursor == "" || len(resp.Records) == 0 { 66 return resp.Records, nil 67 } 68 69 rest, err := listAllPublicKeys(ctx, l, xrpcc, did, *resp.Cursor, pagesLeft-1) 70 if err != nil { 71 return nil, err 72 } 73 74 return append(resp.Records, rest...), nil 75} 76 77func collectPublicKeys(l *slog.Logger, did syntax.DID, records []*comatproto.RepoListRecords_Record) []db.PublicKey { 78 return collectPublicKeysInto(l, did, records, nil) 79} 80 81func collectPublicKeysInto(l *slog.Logger, did syntax.DID, records []*comatproto.RepoListRecords_Record, acc []db.PublicKey) []db.PublicKey { 82 if len(records) == 0 { 83 return acc 84 } 85 86 return collectPublicKeysInto(l, did, records[1:], appendValidKey(l, did, acc, records[0])) 87} 88 89func appendValidKey(l *slog.Logger, did syntax.DID, acc []db.PublicKey, record *comatproto.RepoListRecords_Record) []db.PublicKey { 90 if record == nil { 91 return acc 92 } 93 94 key, ok := record.Value.Val.(*tangled.PublicKey) 95 if !ok || key == nil { 96 return acc 97 } 98 99 rkey, err := recordKeyFromURI(record.Uri) 100 if err != nil { 101 l.Warn("skipping public key with unparseable uri", "uri", record.Uri, "err", err) 102 return acc 103 } 104 105 return append(acc, db.PublicKey{ 106 Did: did, 107 Rkey: rkey, 108 PublicKey: *key, 109 }) 110} 111 112func recordKeyFromURI(uri string) (syntax.RecordKey, error) { 113 aturi, err := syntax.ParseATURI(uri) 114 if err != nil { 115 return "", err 116 } 117 return aturi.RecordKey(), nil 118}