forked from
tangled.org/core
Monorepo for Tangled
1package xrpc
2
3import (
4 "database/sql"
5 "encoding/json"
6 "errors"
7 "log/slog"
8 "net/http"
9 "time"
10
11 "github.com/bluesky-social/indigo/atproto/atclient"
12 "github.com/bluesky-social/indigo/util/ssrf"
13 "github.com/go-chi/chi/v5"
14 "github.com/redis/go-redis/v9"
15 "tangled.org/core/api/tangled"
16 "tangled.org/core/idresolver"
17 "tangled.org/core/knotmirror/config"
18 "tangled.org/core/knotmirror/knotstream"
19 "tangled.org/core/log"
20)
21
22type Xrpc struct {
23 cfg *config.Config
24 db *sql.DB
25 rdb *redis.Client
26 resolver *idresolver.Resolver
27 ks *knotstream.KnotStream
28 logger *slog.Logger
29 httpClient *http.Client
30 inflight *inflightTracker
31}
32
33func New(logger *slog.Logger, cfg *config.Config, db *sql.DB, rdb *redis.Client, resolver *idresolver.Resolver, ks *knotstream.KnotStream) *Xrpc {
34 httpClient := &http.Client{
35 Timeout: 30 * time.Second,
36 }
37 if cfg.KnotSSRF {
38 httpClient.Transport = ssrf.PublicOnlyTransport()
39 }
40 return &Xrpc{
41 cfg: cfg,
42 db: db,
43 rdb: rdb,
44 resolver: resolver,
45 ks: ks,
46 logger: log.SubLogger(logger, "xrpc"),
47 httpClient: httpClient,
48 inflight: newInflightTracker(),
49 }
50}
51
52func (x *Xrpc) Router() http.Handler {
53 r := chi.NewRouter()
54 r.Use(metricsMiddleware)
55
56 r.Group(func(r chi.Router) {
57 r.Use(x.inflight.middleware)
58
59 r.Get("/"+tangled.GitTempGetArchiveNSID, x.GetArchive)
60 r.Get("/"+tangled.GitTempGetBlobNSID, x.GetBlob)
61 r.Get("/"+tangled.GitTempGetBranchNSID, x.GetBranch)
62 // r.Get("/"+tangled.GitTempGetCommitNSID, x.GetCommit) // todo
63 // r.Get("/"+tangled.GitTempGetDiffNSID, x.GetDiff) // todo
64 // r.Get("/"+tangled.GitTempGetEntityNSID, x.GetEntity) // todo
65 // r.Get("/"+tangled.GitTempGetHeadNSID, x.GetHead) // todo
66 r.Get("/"+tangled.GitTempGetTagNSID, x.GetTag) // using types.Response
67 r.Get("/"+tangled.GitTempGetTreeNSID, x.GetTree)
68 r.Get("/"+tangled.GitTempListBranchesNSID, x.ListBranches) // wip, unknown output
69 r.Get("/"+tangled.GitTempListCommitsNSID, x.ListCommits)
70 r.Get("/"+tangled.GitTempListLanguagesNSID, x.ListLanguages)
71 r.Get("/"+tangled.GitTempListTagsNSID, x.ListTags)
72 r.Get("/"+tangled.RepoBlobNSID, x.RepoBlob)
73 r.Post("/"+tangled.SyncRequestCrawlNSID, x.RequestCrawl)
74 })
75
76 return r
77}
78
79func writeJson(w http.ResponseWriter, status int, response any) error {
80 w.Header().Set("Content-Type", "application/json")
81 w.WriteHeader(status)
82 if err := json.NewEncoder(w).Encode(response); err != nil {
83 return err
84 }
85 return nil
86}
87
88func writeErr(w http.ResponseWriter, err error) error {
89 var apiErr *atclient.APIError
90 if errors.As(err, &apiErr) {
91 return writeJson(w, apiErr.StatusCode, atclient.ErrorBody{Name: apiErr.Name, Message: apiErr.Message})
92 }
93 return writeJson(w, http.StatusInternalServerError, atclient.ErrorBody{Name: "InternalServerError", Message: "internal server error"})
94}