This repository has no description
2.6 kB
78 lines
1package repoverify
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8 "time"
9
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/hostutil"
14 "tangled.org/core/idresolver"
15 "tangled.org/core/repoident"
16 "tangled.org/core/xrpc/xrpcclient"
17)
18
19type Result struct {
20 RepoDid repoident.RepoDid
21 OwnerDid repoident.OwnerDid
22 KnotURL repoident.KnotURL
23 // Rkey of the sh.tangled.repo record tracked by the knot; empty when the
24 // knot does not support describeRepo.
25 Rkey syntax.RecordKey
26}
27
28var ErrKnotAnswer = errors.New("knot returned an invalid describeRepo answer")
29
30func Describe(ctx context.Context, httpClient *http.Client, knot repoident.KnotURL, repoDid repoident.RepoDid) (Result, error) {
31 client := &indigoxrpc.Client{Host: knot.String(), Client: httpClient}
32 out, err := tangled.RepoDescribeRepo(ctx, client, repoDid.String())
33 if xrpcErr := xrpcclient.HandleXrpcErr(err); xrpcErr != nil {
34 return Result{}, fmt.Errorf("describeRepo on %s: %w (%v)", knot, xrpcErr, err)
35 }
36 if out.RepoDid != repoDid.String() {
37 return Result{}, fmt.Errorf("%w: knot %s returned repoDid %q, want %q", ErrKnotAnswer, knot, out.RepoDid, repoDid)
38 }
39 ownerDid, err := repoident.NewOwnerDid(out.OwnerDid)
40 if err != nil {
41 return Result{}, fmt.Errorf("%w from knot %s: %w", ErrKnotAnswer, knot, err)
42 }
43 rkey, err := syntax.ParseRecordKey(out.Rkey)
44 if err != nil {
45 return Result{}, fmt.Errorf("%w: knot %s returned rkey %q: %w", ErrKnotAnswer, knot, out.Rkey, err)
46 }
47 return Result{RepoDid: repoDid, OwnerDid: ownerDid, KnotURL: knot, Rkey: rkey}, nil
48}
49
50type Verifier func(ctx context.Context, repoDid repoident.RepoDid) (Result, error)
51
52const verifyTimeout = 10 * time.Second
53
54func New(resolver *idresolver.Resolver, dev bool) Verifier {
55 httpClient := hostutil.SafeClient(dev, verifyTimeout)
56 policy := repoident.SchemeFor(dev)
57
58 return func(ctx context.Context, repoDid repoident.RepoDid) (Result, error) {
59 ctx, cancel := context.WithTimeout(ctx, verifyTimeout)
60 defer cancel()
61
62 ident, err := resolver.ResolveIdent(ctx, repoDid.String())
63 if err != nil {
64 return Result{}, fmt.Errorf("resolve repoDid %s: %w", repoDid, err)
65 }
66
67 knot, err := repoident.KnotURLFromIdentity(ident, policy)
68 if err != nil {
69 return Result{}, fmt.Errorf("repoDid %s: %w", repoDid, err)
70 }
71
72 result, err := Describe(ctx, httpClient, knot, repoDid)
73 if errors.Is(err, xrpcclient.ErrXrpcUnsupported) {
74 return Result{RepoDid: repoDid, KnotURL: knot}, nil
75 }
76 return result, err
77 }
78}