115 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.RepoTree)
80
})
81
82
r.Route("/blob/{ref}", func(r chi.Router) {
83
r.Get("/*", h.Blob)
84
})
85
86
r.Get("/log/{ref}", h.Log)
87
r.Get("/archive/{file}", h.Archive)
88
r.Get("/commit/{ref}", h.Diff)
89
r.Get("/tags", h.Tags)
90
r.Get("/branches", h.Branches)
91
})
92
})
93
94
// Create a new repository.
95
r.Route("/repo", func(r chi.Router) {
96
r.Use(h.VerifySignature)
97
r.Put("/new", h.NewRepo)
98
})
99
100
r.Route("/member", func(r chi.Router) {
101
r.Use(h.VerifySignature)
102
r.Put("/add", h.AddMember)
103
})
104
105
// Initialize the knot with an owner and public key.
106
r.With(h.VerifySignature).Post("/init", h.Init)
107
108
// Health check. Used for two-way verification with appview.
109
r.With(h.VerifySignature).Get("/health", h.Health)
110
111
// All public keys on the knot.
112
r.Get("/keys", h.Keys)
113
114
return r, nil
115
}
116