A vibe coded tangled fork which supports pijul.
1package xrpc
2
3import (
4 "encoding/json"
5 "fmt"
6 "net/http"
7 "strings"
8
9 "github.com/bluesky-social/indigo/api/atproto"
10 "github.com/bluesky-social/indigo/atproto/atclient"
11 "github.com/bluesky-social/indigo/atproto/syntax"
12 "github.com/bluesky-social/indigo/xrpc"
13 "tangled.org/core/api/tangled"
14 "tangled.org/core/knotmirror/db"
15 "tangled.org/core/knotmirror/hostutil"
16 "tangled.org/core/knotmirror/models"
17)
18
19func (x *Xrpc) RequestCrawl(w http.ResponseWriter, r *http.Request) {
20 var input tangled.SyncRequestCrawl_Input
21 if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
22 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: "failed to decode json body"})
23 return
24 }
25
26 ctx := r.Context()
27
28 l := x.logger.With("input", input)
29
30 hostname, noSSL, err := hostutil.ParseHostname(input.Hostname)
31 if err != nil {
32 l.Error("invalid hostname", "err", err)
33 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("hostname field empty or invalid: %s", input.Hostname)})
34 return
35 }
36
37 // TODO: check if host is Knot with knot.describeServer
38
39 // store given repoAt to db
40 // this will allow knotmirror to ingest repo creation event bypassing tap.
41 // this step won't be needed once we introduce did-for-repo
42 // TODO(boltless): remove this section
43 if input.EnsureRepo != nil {
44 repoAt, err := syntax.ParseATURI(*input.EnsureRepo)
45 if err != nil {
46 l.Error("invalid repo at-uri", "err", err)
47 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", *input.EnsureRepo)})
48 return
49 }
50 owner, err := x.resolver.ResolveIdent(ctx, repoAt.Authority().String())
51 if err != nil || owner.Handle.IsInvalidHandle() {
52 l.Error("failed to resolve ident", "err", err, "owner", repoAt.Authority().String())
53 writeErr(w, fmt.Errorf("failed to resolve repo owner"))
54 return
55 }
56 xrpcc := xrpc.Client{Host: owner.PDSEndpoint()}
57 out, err := atproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())
58 if err != nil {
59 l.Error("failed to get repo record", "err", err, "repo", repoAt)
60 writeErr(w, fmt.Errorf("failed to get repo record"))
61 return
62 }
63 record := out.Value.Val.(*tangled.Repo)
64
65 knotUrl := record.Knot
66 if !strings.Contains(record.Knot, "://") {
67 if noSSL {
68 knotUrl = "http://" + knotUrl
69 } else {
70 knotUrl = "https://" + knotUrl
71 }
72 }
73
74 repo := &models.Repo{
75 Did: owner.DID,
76 Rkey: repoAt.RecordKey(),
77 Cid: (*syntax.CID)(out.Cid),
78 Name: record.Name,
79 KnotDomain: knotUrl,
80 State: models.RepoStatePending,
81 ErrorMsg: "",
82 RetryAfter: 0,
83 RetryCount: 0,
84 }
85
86 if err := db.UpsertRepo(ctx, x.db, repo); err != nil {
87 l.Error("failed to upsert repo", "err", err)
88 writeErr(w, err)
89 return
90 }
91 }
92
93 // subscribe to requested host
94 if !x.ks.CheckIfSubscribed(hostname) {
95 if err := x.ks.SubscribeHost(ctx, hostname, noSSL); err != nil {
96 // TODO(boltless): return HostBanned on banned hosts
97 l.Error("failed to subscribe host", "err", err)
98 writeErr(w, err)
99 return
100 }
101 }
102
103 w.WriteHeader(http.StatusOK)
104}