116 lines
2.7 kB
1
package knotserver
2
3
import (
4
"context"
5
"fmt"
6
"log/slog"
7
"net/http"
8
9
"github.com/go-chi/chi/v5"
10
"github.com/sotangled/tangled/jetstream"
11
"github.com/sotangled/tangled/knotserver/config"
12
"github.com/sotangled/tangled/knotserver/db"
13
"github.com/sotangled/tangled/rbac"
14
)
15
16
const (
17
ThisServer = "thisserver" // resource identifier for rbac enforcement
18
)
19
20
type 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
33
func 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.Post("/collaborator/add", h.AddRepoCollaborator)
73
74
r.Get("/", h.RepoIndex)
75
r.Get("/info/refs", h.InfoRefs)
76
r.Post("/git-upload-pack", h.UploadPack)
77
78
r.Route("/tree/{ref}", func(r chi.Router) {
79
r.Get("/", h.RepoIndex)
80
r.Get("/*", h.RepoTree)
81
})
82
83
r.Route("/blob/{ref}", func(r chi.Router) {
84
r.Get("/*", h.Blob)
85
})
86
87
r.Get("/log/{ref}", h.Log)
88
r.Get("/archive/{file}", h.Archive)
89
r.Get("/commit/{ref}", h.Diff)
90
r.Get("/tags", h.Tags)
91
r.Get("/branches", h.Branches)
92
})
93
})
94
95
// Create a new repository.
96
r.Route("/repo", func(r chi.Router) {
97
r.Use(h.VerifySignature)
98
r.Put("/new", h.NewRepo)
99
})
100
101
r.Route("/member", func(r chi.Router) {
102
r.Use(h.VerifySignature)
103
r.Put("/add", h.AddMember)
104
})
105
106
// Initialize the knot with an owner and public key.
107
r.With(h.VerifySignature).Post("/init", h.Init)
108
109
// Health check. Used for two-way verification with appview.
110
r.With(h.VerifySignature).Get("/health", h.Health)
111
112
// All public keys on the knot.
113
r.Get("/keys", h.Keys)
114
115
return r, nil
116
}
117