+1
-2
flake.nix
+1
-2
flake.nix
+6
knotserver/db/init.go
+6
knotserver/db/init.go
···
25
created timestamp default current_timestamp,
26
unique(did, name, key)
27
);
28
+
create table if not exists users (
29
+
id integer primary key autoincrement,
30
+
did text not null,
31
+
unique(did),
32
+
foreign key (did) references public_keys(did) on delete cascade
33
+
);
34
create table if not exists repos (
35
id integer primary key autoincrement,
36
did text not null,
+2
-2
knotserver/db/pubkeys.go
+2
-2
knotserver/db/pubkeys.go
···
11
tangled.PublicKey
12
}
13
14
-
func (d *DB) AddPublicKeyFromRecord(recordIface map[string]interface{}) error {
15
record := make(map[string]string)
16
for k, v := range recordIface {
17
if str, ok := v.(string); ok {
···
20
}
21
22
pk := PublicKey{
23
-
Did: record["did"],
24
}
25
pk.Name = record["name"]
26
pk.Key = record["key"]
···
11
tangled.PublicKey
12
}
13
14
+
func (d *DB) AddPublicKeyFromRecord(did string, recordIface map[string]interface{}) error {
15
record := make(map[string]string)
16
for k, v := range recordIface {
17
if str, ok := v.(string); ok {
···
20
}
21
22
pk := PublicKey{
23
+
Did: did,
24
}
25
pk.Name = record["name"]
26
pk.Key = record["key"]
+11
knotserver/db/users.go
+11
knotserver/db/users.go
+21
-4
knotserver/handler.go
+21
-4
knotserver/handler.go
···
18
c *config.Config
19
db *db.DB
20
js *jsclient.JetstreamClient
21
}
22
23
func Setup(ctx context.Context, c *config.Config, db *db.DB) (http.Handler, error) {
24
r := chi.NewRouter()
25
26
h := Handle{
27
-
c: c,
28
-
db: db,
29
}
30
31
err := h.StartJetstream(ctx)
···
33
return nil, fmt.Errorf("failed to start jetstream: %w", err)
34
}
35
36
r.Get("/", h.Index)
37
r.Route("/{did}", func(r chi.Router) {
38
// Repo routes
···
63
})
64
65
// Add a new user to the knot
66
-
// r.With(h.VerifySignature).Put("/user", h.AddUser)
67
68
// Health check. Used for two-way verification with appview.
69
r.With(h.VerifySignature).Get("/health", h.Health)
···
85
}
86
87
go func() {
88
for msg := range messages {
89
var data map[string]interface{}
90
if err := json.Unmarshal(msg, &data); err != nil {
···
97
98
switch commit["collection"].(string) {
99
case tangled.PublicKeyNSID:
100
record := commit["record"].(map[string]interface{})
101
-
if err := h.db.AddPublicKeyFromRecord(record); err != nil {
102
log.Printf("failed to add public key: %v", err)
103
}
104
log.Printf("added public key from firehose: %s", data["did"])
···
18
c *config.Config
19
db *db.DB
20
js *jsclient.JetstreamClient
21
+
22
+
// init is a channel that is closed when the knot has been initailized
23
+
// i.e. when the first user (knot owner) has been added.
24
+
init chan struct{}
25
+
knotInitialized bool
26
}
27
28
func Setup(ctx context.Context, c *config.Config, db *db.DB) (http.Handler, error) {
29
r := chi.NewRouter()
30
31
h := Handle{
32
+
c: c,
33
+
db: db,
34
+
init: make(chan struct{}),
35
}
36
37
err := h.StartJetstream(ctx)
···
39
return nil, fmt.Errorf("failed to start jetstream: %w", err)
40
}
41
42
+
// TODO: close this channel and set h.knotInitialized *only after*
43
+
// checking if we have an owner.
44
+
close(h.init)
45
+
h.knotInitialized = true
46
+
47
r.Get("/", h.Index)
48
r.Route("/{did}", func(r chi.Router) {
49
// Repo routes
···
74
})
75
76
// Add a new user to the knot
77
+
r.With(h.VerifySignature).Put("/user", h.AddUser)
78
+
r.With(h.VerifySignature).Post("/init", h.Init)
79
80
// Health check. Used for two-way verification with appview.
81
r.With(h.VerifySignature).Get("/health", h.Health)
···
97
}
98
99
go func() {
100
+
log.Println("waiting for knot to be initialized")
101
+
<-h.init
102
+
log.Println("initalized jetstream watcher")
103
+
104
for msg := range messages {
105
var data map[string]interface{}
106
if err := json.Unmarshal(msg, &data); err != nil {
···
113
114
switch commit["collection"].(string) {
115
case tangled.PublicKeyNSID:
116
+
did := data["did"].(string)
117
record := commit["record"].(map[string]interface{})
118
+
if err := h.db.AddPublicKeyFromRecord(did, record); err != nil {
119
log.Printf("failed to add public key: %v", err)
120
}
121
log.Printf("added public key from firehose: %s", data["did"])
-2
knotserver/jsclient/jetstream.go
-2
knotserver/jsclient/jetstream.go
+77
knotserver/routes.go
+77
knotserver/routes.go
···
391
w.WriteHeader(http.StatusNoContent)
392
}
393
394
+
func (h *Handle) AddUser(w http.ResponseWriter, r *http.Request) {
395
+
data := struct {
396
+
DID string `json:"did"`
397
+
PublicKey string `json:"pubkey"`
398
+
}{}
399
+
400
+
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
401
+
writeError(w, "invalid request body", http.StatusBadRequest)
402
+
return
403
+
}
404
+
405
+
did := data.DID
406
+
key := data.PublicKey
407
+
408
+
if err := h.db.AddUser(did); err == nil {
409
+
pk := db.PublicKey{
410
+
Did: did,
411
+
}
412
+
pk.Key = key
413
+
pk.Name = "default"
414
+
err := h.db.AddPublicKey(pk)
415
+
if err != nil {
416
+
writeError(w, err.Error(), http.StatusInternalServerError)
417
+
return
418
+
}
419
+
} else {
420
+
writeError(w, err.Error(), http.StatusInternalServerError)
421
+
return
422
+
}
423
+
424
+
h.js.UpdateDids([]string{did})
425
+
426
+
w.WriteHeader(http.StatusNoContent)
427
+
}
428
+
429
+
// TODO: make this set the initial user as the owner
430
+
func (h *Handle) Init(w http.ResponseWriter, r *http.Request) {
431
+
if h.knotInitialized {
432
+
writeError(w, "knot already initialized", http.StatusConflict)
433
+
return
434
+
}
435
+
436
+
data := struct {
437
+
DID string `json:"did"`
438
+
PublicKey string `json:"pubkey"`
439
+
}{}
440
+
441
+
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
442
+
writeError(w, "invalid request body", http.StatusBadRequest)
443
+
return
444
+
}
445
+
446
+
did := data.DID
447
+
key := data.PublicKey
448
+
449
+
if err := h.db.AddUser(did); err == nil {
450
+
pk := db.PublicKey{
451
+
Did: did,
452
+
}
453
+
pk.Key = key
454
+
pk.Name = "default"
455
+
err := h.db.AddPublicKey(pk)
456
+
if err != nil {
457
+
writeError(w, err.Error(), http.StatusInternalServerError)
458
+
return
459
+
}
460
+
} else {
461
+
writeError(w, err.Error(), http.StatusInternalServerError)
462
+
return
463
+
}
464
+
465
+
h.js.UpdateDids([]string{did})
466
+
// Signal that the knot is ready
467
+
close(h.init)
468
+
w.WriteHeader(http.StatusNoContent)
469
+
}
470
+
471
func (h *Handle) Health(w http.ResponseWriter, r *http.Request) {
472
log.Println("got health check")
473
mac := hmac.New(sha256.New, []byte(h.c.Server.Secret))