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 "github.com/sotangled/tangled/knotserver/config"
11 "github.com/sotangled/tangled/knotserver/db"
12 "github.com/sotangled/tangled/knotserver/jsclient"
13 "github.com/sotangled/tangled/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 js *jsclient.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, 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 init: make(chan struct{}),
42 }
43
44 err := e.AddDomain(ThisServer)
45 if err != nil {
46 return nil, fmt.Errorf("failed to setup enforcer: %w", err)
47 }
48
49 err = h.StartJetstream(ctx)
50 if err != nil {
51 return nil, fmt.Errorf("failed to start jetstream: %w", err)
52 }
53
54 // Check if the knot knows about any Dids;
55 // if it does, it is already initialized and we can repopulate the
56 // Jetstream subscriptions.
57 dids, err := db.GetAllDids()
58 if err != nil {
59 return nil, fmt.Errorf("failed to get all Dids: %w", err)
60 }
61 if len(dids) > 0 {
62 h.knotInitialized = true
63 close(h.init)
64 h.js.UpdateDids(dids)
65 }
66
67 r.Get("/", h.Index)
68 r.Route("/{did}", func(r chi.Router) {
69 // Repo routes
70 r.Route("/{name}", func(r chi.Router) {
71 r.Post("/collaborator/add", h.AddRepoCollaborator)
72
73 r.Get("/", h.RepoIndex)
74 r.Get("/info/refs", h.InfoRefs)
75 r.Post("/git-upload-pack", h.UploadPack)
76
77 r.Route("/tree/{ref}", func(r chi.Router) {
78 r.Get("/*", h.RepoTree)
79 })
80
81 r.Route("/blob/{ref}", func(r chi.Router) {
82 r.Get("/*", h.FileContent)
83 })
84
85 r.Get("/log/{ref}", h.Log)
86 r.Get("/archive/{file}", h.Archive)
87 r.Get("/commit/{ref}", h.Diff)
88 r.Get("/refs/", h.Refs)
89 })
90 })
91
92 // Create a new repository.
93 r.Route("/repo", func(r chi.Router) {
94 r.Use(h.VerifySignature)
95 r.Put("/new", h.NewRepo)
96 })
97
98 r.Route("/member", func(r chi.Router) {
99 r.Use(h.VerifySignature)
100 r.Put("/add", h.AddMember)
101 })
102
103 // Initialize the knot with an owner and public key.
104 r.With(h.VerifySignature).Post("/init", h.Init)
105
106 // Health check. Used for two-way verification with appview.
107 r.With(h.VerifySignature).Get("/health", h.Health)
108
109 // All public keys on the knot.
110 r.Get("/keys", h.Keys)
111
112 return r, nil
113}