forked from tangled.org/core
this repo has no description
at master 3.6 kB view raw
1package knotserver 2 3import ( 4 "context" 5 "fmt" 6 "log/slog" 7 "net/http" 8 9 "github.com/go-chi/chi/v5" 10 "tangled.sh/tangled.sh/core/idresolver" 11 "tangled.sh/tangled.sh/core/jetstream" 12 "tangled.sh/tangled.sh/core/knotserver/config" 13 "tangled.sh/tangled.sh/core/knotserver/db" 14 "tangled.sh/tangled.sh/core/knotserver/xrpc" 15 tlog "tangled.sh/tangled.sh/core/log" 16 "tangled.sh/tangled.sh/core/notifier" 17 "tangled.sh/tangled.sh/core/rbac" 18 "tangled.sh/tangled.sh/core/xrpc/serviceauth" 19) 20 21type Knot struct { 22 c *config.Config 23 db *db.DB 24 jc *jetstream.JetstreamClient 25 e *rbac.Enforcer 26 l *slog.Logger 27 n *notifier.Notifier 28 resolver *idresolver.Resolver 29} 30 31func Setup(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, jc *jetstream.JetstreamClient, l *slog.Logger, n *notifier.Notifier) (http.Handler, error) { 32 r := chi.NewRouter() 33 34 h := Knot{ 35 c: c, 36 db: db, 37 e: e, 38 l: l, 39 jc: jc, 40 n: n, 41 resolver: idresolver.DefaultResolver(), 42 } 43 44 err := e.AddKnot(rbac.ThisServer) 45 if err != nil { 46 return nil, fmt.Errorf("failed to setup enforcer: %w", err) 47 } 48 49 // configure owner 50 if err = h.configureOwner(); err != nil { 51 return nil, err 52 } 53 h.l.Info("owner set", "did", h.c.Server.Owner) 54 h.jc.AddDid(h.c.Server.Owner) 55 56 // configure known-dids in jetstream consumer 57 dids, err := h.db.GetAllDids() 58 if err != nil { 59 return nil, fmt.Errorf("failed to get all dids: %w", err) 60 } 61 for _, d := range dids { 62 jc.AddDid(d) 63 } 64 65 err = h.jc.StartJetstream(ctx, h.processMessages) 66 if err != nil { 67 return nil, fmt.Errorf("failed to start jetstream: %w", err) 68 } 69 70 r.Get("/", func(w http.ResponseWriter, r *http.Request) { 71 w.Write([]byte("This is a knot server. More info at https://tangled.sh")) 72 }) 73 74 r.Route("/{did}", func(r chi.Router) { 75 r.Route("/{name}", func(r chi.Router) { 76 // routes for git operations 77 r.Get("/info/refs", h.InfoRefs) 78 r.Post("/git-upload-pack", h.UploadPack) 79 r.Post("/git-receive-pack", h.ReceivePack) 80 }) 81 }) 82 83 // xrpc apis 84 r.Mount("/xrpc", h.XrpcRouter()) 85 86 // Socket that streams git oplogs 87 r.Get("/events", h.Events) 88 89 return r, nil 90} 91 92func (h *Knot) XrpcRouter() http.Handler { 93 logger := tlog.New("knots") 94 95 serviceAuth := serviceauth.NewServiceAuth(h.l, h.resolver, h.c.Server.Did().String()) 96 97 xrpc := &xrpc.Xrpc{ 98 Config: h.c, 99 Db: h.db, 100 Ingester: h.jc, 101 Enforcer: h.e, 102 Logger: logger, 103 Notifier: h.n, 104 Resolver: h.resolver, 105 ServiceAuth: serviceAuth, 106 } 107 return xrpc.Router() 108} 109 110func (h *Knot) configureOwner() error { 111 cfgOwner := h.c.Server.Owner 112 113 rbacDomain := "thisserver" 114 115 existing, err := h.e.GetKnotUsersByRole("server:owner", rbacDomain) 116 if err != nil { 117 return err 118 } 119 120 switch len(existing) { 121 case 0: 122 // no owner configured, continue 123 case 1: 124 // find existing owner 125 existingOwner := existing[0] 126 127 // no ownership change, this is okay 128 if existingOwner == h.c.Server.Owner { 129 break 130 } 131 132 // remove existing owner 133 if err = h.db.RemoveDid(existingOwner); err != nil { 134 return err 135 } 136 if err = h.e.RemoveKnotOwner(rbacDomain, existingOwner); err != nil { 137 return err 138 } 139 140 default: 141 return fmt.Errorf("more than one owner in DB, try deleting %q and starting over", h.c.Server.DBPath) 142 } 143 144 if err = h.db.AddDid(cfgOwner); err != nil { 145 return fmt.Errorf("failed to add owner to DB: %w", err) 146 } 147 if err := h.e.AddKnotOwner(rbacDomain, cfgOwner); err != nil { 148 return fmt.Errorf("failed to add owner to RBAC: %w", err) 149 } 150 151 return nil 152}