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