forked from tangled.org/core
this repo has no description
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/jetstream" 11 "tangled.sh/tangled.sh/core/knotserver/config" 12 "tangled.sh/tangled.sh/core/knotserver/db" 13 "tangled.sh/tangled.sh/core/rbac" 14) 15 16const ( 17 ThisServer = "thisserver" // resource identifier for rbac enforcement 18) 19 20type Handle struct { 21 c *config.Config 22 db *db.DB 23 jc *jetstream.JetstreamClient 24 e *rbac.Enforcer 25 l *slog.Logger 26 27 // init is a channel that is closed when the knot has been initailized 28 // i.e. when the first user (knot owner) has been added. 29 init chan struct{} 30 knotInitialized bool 31} 32 33func Setup(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, jc *jetstream.JetstreamClient, l *slog.Logger) (http.Handler, error) { 34 r := chi.NewRouter() 35 36 h := Handle{ 37 c: c, 38 db: db, 39 e: e, 40 l: l, 41 jc: jc, 42 init: make(chan struct{}), 43 } 44 45 err := e.AddDomain(ThisServer) 46 if err != nil { 47 return nil, fmt.Errorf("failed to setup enforcer: %w", err) 48 } 49 50 err = h.jc.StartJetstream(ctx, h.processMessages) 51 if err != nil { 52 return nil, fmt.Errorf("failed to start jetstream: %w", err) 53 } 54 55 // Check if the knot knows about any Dids; 56 // if it does, it is already initialized and we can repopulate the 57 // Jetstream subscriptions. 58 dids, err := db.GetAllDids() 59 if err != nil { 60 return nil, fmt.Errorf("failed to get all Dids: %w", err) 61 } 62 if len(dids) > 0 { 63 h.knotInitialized = true 64 close(h.init) 65 // h.jc.UpdateDids(dids) 66 } 67 68 r.Get("/", h.Index) 69 r.Route("/{did}", func(r chi.Router) { 70 // Repo routes 71 r.Route("/{name}", func(r chi.Router) { 72 r.Route("/collaborator", func(r chi.Router) { 73 r.Use(h.VerifySignature) 74 r.Post("/add", h.AddRepoCollaborator) 75 }) 76 77 r.Get("/", h.RepoIndex) 78 r.Get("/info/refs", h.InfoRefs) 79 r.Post("/git-upload-pack", h.UploadPack) 80 81 r.Route("/merge", func(r chi.Router) { 82 r.With(h.VerifySignature) 83 r.Post("/", h.Merge) 84 r.Post("/check", h.MergeCheck) 85 }) 86 87 r.Route("/tree/{ref}", func(r chi.Router) { 88 r.Get("/", h.RepoIndex) 89 r.Get("/*", h.RepoTree) 90 }) 91 92 r.Route("/blob/{ref}", func(r chi.Router) { 93 r.Get("/*", h.Blob) 94 }) 95 96 r.Get("/log/{ref}", h.Log) 97 r.Get("/archive/{file}", h.Archive) 98 r.Get("/commit/{ref}", h.Diff) 99 r.Get("/tags", h.Tags) 100 r.Get("/branches", h.Branches) 101 }) 102 }) 103 104 // Create a new repository. 105 r.Route("/repo", func(r chi.Router) { 106 r.Use(h.VerifySignature) 107 r.Put("/new", h.NewRepo) 108 r.Delete("/", h.RemoveRepo) 109 }) 110 111 r.Route("/member", func(r chi.Router) { 112 r.Use(h.VerifySignature) 113 r.Put("/add", h.AddMember) 114 }) 115 116 // Initialize the knot with an owner and public key. 117 r.With(h.VerifySignature).Post("/init", h.Init) 118 119 // Health check. Used for two-way verification with appview. 120 r.With(h.VerifySignature).Get("/health", h.Health) 121 122 // All public keys on the knot. 123 r.Get("/keys", h.Keys) 124 125 return r, nil 126}