+15
-12
cmd/knotserver/main.go
+15
-12
cmd/knotserver/main.go
···
3
import (
4
"context"
5
"fmt"
6
-
"log"
7
-
"log/slog"
8
"net/http"
9
-
"os"
10
11
"github.com/sotangled/tangled/knotserver"
12
"github.com/sotangled/tangled/knotserver/config"
13
"github.com/sotangled/tangled/knotserver/db"
14
"github.com/sotangled/tangled/rbac"
15
)
16
···
19
// ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
20
// defer stop()
21
22
-
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))
23
24
c, err := config.Load(ctx)
25
if err != nil {
26
-
log.Fatal(err)
27
}
28
29
if c.Server.Dev {
30
-
log.Println("running in dev mode, signature verification is disabled")
31
}
32
33
db, err := db.Setup(c.Server.DBPath)
34
if err != nil {
35
-
log.Fatalf("failed to setup db: %s", err)
36
}
37
38
e, err := rbac.NewEnforcer(c.Server.DBPath)
39
if err != nil {
40
-
log.Fatalf("failed to setup rbac enforcer: %s", err)
41
}
42
43
-
mux, err := knotserver.Setup(ctx, c, db, e)
44
if err != nil {
45
-
log.Fatal(err)
46
}
47
48
addr := fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
49
50
-
log.Println("starting main server on", addr)
51
-
log.Fatal(http.ListenAndServe(addr, mux))
52
}
···
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
8
"github.com/sotangled/tangled/knotserver"
9
"github.com/sotangled/tangled/knotserver/config"
10
"github.com/sotangled/tangled/knotserver/db"
11
+
"github.com/sotangled/tangled/log"
12
"github.com/sotangled/tangled/rbac"
13
)
14
···
17
// ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
18
// defer stop()
19
20
+
l := log.New("knotserver")
21
22
c, err := config.Load(ctx)
23
if err != nil {
24
+
l.Error("failed to load config", "error", err)
25
+
return
26
}
27
28
if c.Server.Dev {
29
+
l.Info("running in dev mode, signature verification is disabled")
30
}
31
32
db, err := db.Setup(c.Server.DBPath)
33
if err != nil {
34
+
l.Error("failed to setup db", "error", err)
35
+
return
36
}
37
38
e, err := rbac.NewEnforcer(c.Server.DBPath)
39
if err != nil {
40
+
l.Error("failed to setup rbac enforcer", "error", err)
41
+
return
42
}
43
44
+
mux, err := knotserver.Setup(ctx, c, db, e, l)
45
if err != nil {
46
+
l.Error("failed to setup server", "error", err)
47
+
return
48
}
49
50
addr := fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port)
51
52
+
l.Info("starting main server", "address", addr)
53
+
l.Error("server error", "error", http.ListenAndServe(addr, mux))
54
+
return
55
}
+3
-3
knotserver/file.go
+3
-3
knotserver/file.go
···
3
import (
4
"bytes"
5
"io"
6
-
"log"
7
"net/http"
8
"strings"
9
···
43
}
44
}
45
46
-
func (h *Handle) showFile(content string, data map[string]any, w http.ResponseWriter) {
47
lc, err := countLines(strings.NewReader(content))
48
if err != nil {
49
// Non-fatal, we'll just skip showing line numbers in the template.
50
-
log.Printf("counting lines: %s", err)
51
}
52
53
lines := make([]int, lc)
···
3
import (
4
"bytes"
5
"io"
6
+
"log/slog"
7
"net/http"
8
"strings"
9
···
43
}
44
}
45
46
+
func (h *Handle) showFile(content string, data map[string]any, w http.ResponseWriter, l *slog.Logger) {
47
lc, err := countLines(strings.NewReader(content))
48
if err != nil {
49
// Non-fatal, we'll just skip showing line numbers in the template.
50
+
l.Warn("counting lines", "error", err)
51
}
52
53
lines := make([]int, lc)
+3
-4
knotserver/git.go
+3
-4
knotserver/git.go
···
3
import (
4
"compress/gzip"
5
"io"
6
-
"log"
7
"net/http"
8
"path/filepath"
9
···
26
27
if err := cmd.InfoRefs(); err != nil {
28
http.Error(w, err.Error(), 500)
29
-
log.Printf("git: failed to execute git-upload-pack (info/refs) %s", err)
30
return
31
}
32
}
···
53
reader, err := gzip.NewReader(r.Body)
54
if err != nil {
55
http.Error(w, err.Error(), 500)
56
-
log.Printf("git: failed to create gzip reader: %s", err)
57
return
58
}
59
defer reader.Close()
···
62
cmd.Stdin = reader
63
if err := cmd.UploadPack(); err != nil {
64
http.Error(w, err.Error(), 500)
65
-
log.Printf("git: failed to execute git-upload-pack %s", err)
66
return
67
}
68
}
···
3
import (
4
"compress/gzip"
5
"io"
6
"net/http"
7
"path/filepath"
8
···
25
26
if err := cmd.InfoRefs(); err != nil {
27
http.Error(w, err.Error(), 500)
28
+
d.l.Error("git: failed to execute git-upload-pack (info/refs)", "handler", "InfoRefs", "error", err)
29
return
30
}
31
}
···
52
reader, err := gzip.NewReader(r.Body)
53
if err != nil {
54
http.Error(w, err.Error(), 500)
55
+
d.l.Error("git: failed to create gzip reader", "handler", "UploadPack", "error", err)
56
return
57
}
58
defer reader.Close()
···
61
cmd.Stdin = reader
62
if err := cmd.UploadPack(); err != nil {
63
http.Error(w, err.Error(), 500)
64
+
d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err)
65
return
66
}
67
}
+4
-1
knotserver/handler.go
+4
-1
knotserver/handler.go
···
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
8
"github.com/go-chi/chi/v5"
···
21
db *db.DB
22
js *jsclient.JetstreamClient
23
e *rbac.Enforcer
24
25
// init is a channel that is closed when the knot has been initailized
26
// i.e. when the first user (knot owner) has been added.
···
28
knotInitialized bool
29
}
30
31
-
func Setup(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer) (http.Handler, error) {
32
r := chi.NewRouter()
33
34
h := Handle{
35
c: c,
36
db: db,
37
e: e,
38
init: make(chan struct{}),
39
}
40
···
3
import (
4
"context"
5
"fmt"
6
+
"log/slog"
7
"net/http"
8
9
"github.com/go-chi/chi/v5"
···
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.
···
30
knotInitialized bool
31
}
32
33
+
func 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
+66
-34
knotserver/jetstream.go
+66
-34
knotserver/jetstream.go
···
5
"encoding/json"
6
"fmt"
7
"io"
8
-
"log"
9
"net/http"
10
"net/url"
11
"strings"
···
14
"github.com/sotangled/tangled/api/tangled"
15
"github.com/sotangled/tangled/knotserver/db"
16
"github.com/sotangled/tangled/knotserver/jsclient"
17
)
18
19
func (h *Handle) StartJetstream(ctx context.Context) error {
20
collections := []string{tangled.PublicKeyNSID, tangled.KnotMemberNSID}
21
dids := []string{}
22
23
-
lastTimeUs, err := h.getLastTimeUs()
24
if err != nil {
25
return err
26
}
···
31
return fmt.Errorf("failed to read from jetstream: %w", err)
32
}
33
34
-
go h.processMessages(messages)
35
36
return nil
37
}
38
39
-
func (h *Handle) getLastTimeUs() (int64, error) {
40
lastTimeUs, err := h.db.GetLastTimeUs()
41
if err != nil {
42
-
log.Println("couldn't get last time us, starting from now")
43
lastTimeUs = time.Now().UnixMicro()
44
}
45
46
// If last time is older than a week, start from now
47
if time.Now().UnixMicro()-lastTimeUs > 7*24*60*60*1000*1000 {
48
lastTimeUs = time.Now().UnixMicro()
49
-
log.Printf("last time us is older than a week. discarding that and starting from now.")
50
err = h.db.SaveLastTimeUs(lastTimeUs)
51
if err != nil {
52
-
log.Println("failed to save last time us")
53
}
54
}
55
56
-
log.Printf("found last time_us %d", lastTimeUs)
57
return lastTimeUs, nil
58
}
59
60
-
func (h *Handle) processPublicKey(did string, record map[string]interface{}) {
61
if err := h.db.AddPublicKeyFromRecord(did, record); err != nil {
62
-
log.Printf("failed to add public key: %v", err)
63
-
} else {
64
-
log.Printf("added public key from firehose: %s", did)
65
}
66
}
67
68
-
func (h *Handle) fetchAndAddKeys(did string) {
69
keysEndpoint, err := url.JoinPath(h.c.AppViewEndpoint, "keys", did)
70
if err != nil {
71
-
log.Printf("error building endpoint url: %s: %v", did, err)
72
-
return
73
}
74
75
resp, err := http.Get(keysEndpoint)
76
if err != nil {
77
-
log.Printf("error getting keys for %s: %v", did, err)
78
-
return
79
}
80
defer resp.Body.Close()
81
82
plaintext, err := io.ReadAll(resp.Body)
83
if err != nil {
84
-
log.Printf("error reading response body: %v", err)
85
-
return
86
}
87
88
for _, key := range strings.Split(string(plaintext), "\n") {
···
94
}
95
pk.Key = key
96
if err := h.db.AddPublicKey(pk); err != nil {
97
-
log.Printf("failed to add public key: %v", err)
98
}
99
}
100
}
101
102
-
func (h *Handle) processKnotMember(did string, record map[string]interface{}) {
103
ok, err := h.e.E.Enforce(did, ThisServer, ThisServer, "server:invite")
104
if err != nil || !ok {
105
-
log.Printf("failed to add member from did %s", did)
106
-
return
107
}
108
109
-
log.Printf("adding member")
110
if err := h.e.AddMember(ThisServer, record["member"].(string)); err != nil {
111
-
log.Printf("failed to add member: %v", err)
112
-
} else {
113
-
log.Printf("added member from firehose: %s", record["member"])
114
}
115
116
-
h.fetchAndAddKeys(did)
117
h.js.UpdateDids([]string{did})
118
}
119
120
-
func (h *Handle) processMessages(messages <-chan []byte) {
121
<-h.init
122
-
log.Println("initalized jetstream watcher")
123
124
for msg := range messages {
125
var data map[string]interface{}
126
if err := json.Unmarshal(msg, &data); err != nil {
127
-
log.Printf("error unmarshaling message: %v", err)
128
continue
129
}
130
···
133
did := data["did"].(string)
134
record := commit["record"].(map[string]interface{})
135
136
switch commit["collection"].(string) {
137
case tangled.PublicKeyNSID:
138
-
h.processPublicKey(did, record)
139
case tangled.KnotMemberNSID:
140
-
h.processKnotMember(did, record)
141
}
142
143
lastTimeUs := int64(data["time_us"].(float64))
144
if err := h.db.SaveLastTimeUs(lastTimeUs); err != nil {
145
-
log.Printf("failed to save last time us: %v", err)
146
}
147
}
148
}
···
5
"encoding/json"
6
"fmt"
7
"io"
8
"net/http"
9
"net/url"
10
"strings"
···
13
"github.com/sotangled/tangled/api/tangled"
14
"github.com/sotangled/tangled/knotserver/db"
15
"github.com/sotangled/tangled/knotserver/jsclient"
16
+
"github.com/sotangled/tangled/log"
17
)
18
19
func (h *Handle) StartJetstream(ctx context.Context) error {
20
+
l := h.l.With("component", "jetstream")
21
+
ctx = log.IntoContext(ctx, l)
22
collections := []string{tangled.PublicKeyNSID, tangled.KnotMemberNSID}
23
dids := []string{}
24
25
+
lastTimeUs, err := h.getLastTimeUs(ctx)
26
if err != nil {
27
return err
28
}
···
33
return fmt.Errorf("failed to read from jetstream: %w", err)
34
}
35
36
+
go h.processMessages(ctx, messages)
37
38
return nil
39
}
40
41
+
func (h *Handle) getLastTimeUs(ctx context.Context) (int64, error) {
42
+
l := log.FromContext(ctx)
43
lastTimeUs, err := h.db.GetLastTimeUs()
44
if err != nil {
45
+
l.Info("couldn't get last time us, starting from now")
46
lastTimeUs = time.Now().UnixMicro()
47
}
48
49
// If last time is older than a week, start from now
50
if time.Now().UnixMicro()-lastTimeUs > 7*24*60*60*1000*1000 {
51
lastTimeUs = time.Now().UnixMicro()
52
+
l.Info("last time us is older than a week. discarding that and starting from now")
53
err = h.db.SaveLastTimeUs(lastTimeUs)
54
if err != nil {
55
+
l.Error("failed to save last time us")
56
}
57
}
58
59
+
l.Info("found last time_us", "time_us", lastTimeUs)
60
return lastTimeUs, nil
61
}
62
63
+
func (h *Handle) processPublicKey(ctx context.Context, did string, record map[string]interface{}) error {
64
+
l := log.FromContext(ctx)
65
if err := h.db.AddPublicKeyFromRecord(did, record); err != nil {
66
+
l.Error("failed to add public key", "error", err)
67
+
return fmt.Errorf("failed to add public key: %w", err)
68
}
69
+
l.Info("added public key from firehose", "did", did)
70
+
return nil
71
}
72
73
+
func (h *Handle) fetchAndAddKeys(ctx context.Context, did string) error {
74
+
l := log.FromContext(ctx)
75
+
76
keysEndpoint, err := url.JoinPath(h.c.AppViewEndpoint, "keys", did)
77
if err != nil {
78
+
l.Error("error building endpoint url", "did", did, "error", err.Error())
79
+
return fmt.Errorf("error building endpoint url: %w", err)
80
}
81
82
resp, err := http.Get(keysEndpoint)
83
if err != nil {
84
+
l.Error("error getting keys", "did", did, "error", err)
85
+
return fmt.Errorf("error getting keys: %w", err)
86
}
87
defer resp.Body.Close()
88
89
plaintext, err := io.ReadAll(resp.Body)
90
if err != nil {
91
+
l.Error("error reading response body", "error", err)
92
+
return fmt.Errorf("error reading response body: %w", err)
93
}
94
95
for _, key := range strings.Split(string(plaintext), "\n") {
···
101
}
102
pk.Key = key
103
if err := h.db.AddPublicKey(pk); err != nil {
104
+
l.Error("failed to add public key", "error", err)
105
+
return fmt.Errorf("failed to add public key: %w", err)
106
}
107
}
108
+
return nil
109
}
110
111
+
func (h *Handle) processKnotMember(ctx context.Context, did string, record map[string]interface{}) error {
112
+
l := log.FromContext(ctx)
113
ok, err := h.e.E.Enforce(did, ThisServer, ThisServer, "server:invite")
114
if err != nil || !ok {
115
+
l.Error("failed to add member", "did", did)
116
+
return fmt.Errorf("failed to enforce permissions: %w", err)
117
}
118
119
+
l.Info("adding member")
120
if err := h.e.AddMember(ThisServer, record["member"].(string)); err != nil {
121
+
l.Error("failed to add member", "error", err)
122
+
return fmt.Errorf("failed to add member: %w", err)
123
+
}
124
+
l.Info("added member from firehose", "member", record["member"])
125
+
126
+
if err := h.db.AddDid(did); err != nil {
127
+
l.Error("failed to add did", "error", err)
128
+
return fmt.Errorf("failed to add did: %w", err)
129
}
130
131
+
if err := h.fetchAndAddKeys(ctx, did); err != nil {
132
+
return fmt.Errorf("failed to fetch and add keys: %w", err)
133
+
}
134
+
135
h.js.UpdateDids([]string{did})
136
+
return nil
137
}
138
139
+
func (h *Handle) processMessages(ctx context.Context, messages <-chan []byte) {
140
+
l := log.FromContext(ctx)
141
+
l.Info("waiting for knot to be initialized")
142
<-h.init
143
+
l.Info("initialized jetstream watcher")
144
145
for msg := range messages {
146
var data map[string]interface{}
147
if err := json.Unmarshal(msg, &data); err != nil {
148
+
l.Error("error unmarshaling message", "error", err)
149
continue
150
}
151
···
154
did := data["did"].(string)
155
record := commit["record"].(map[string]interface{})
156
157
+
var processErr error
158
switch commit["collection"].(string) {
159
case tangled.PublicKeyNSID:
160
+
if err := h.processPublicKey(ctx, did, record); err != nil {
161
+
processErr = fmt.Errorf("failed to process public key: %w", err)
162
+
}
163
case tangled.KnotMemberNSID:
164
+
if err := h.processKnotMember(ctx, did, record); err != nil {
165
+
processErr = fmt.Errorf("failed to process knot member: %w", err)
166
+
}
167
+
}
168
+
169
+
if processErr != nil {
170
+
l.Error("error processing message", "error", processErr)
171
+
continue
172
}
173
174
lastTimeUs := int64(data["time_us"].(float64))
175
if err := h.db.SaveLastTimeUs(lastTimeUs); err != nil {
176
+
l.Error("failed to save last time us", "error", err)
177
+
continue
178
}
179
}
180
}
+45
-22
knotserver/routes.go
+45
-22
knotserver/routes.go
···
9
"errors"
10
"fmt"
11
"html/template"
12
-
"log"
13
"net/http"
14
"path/filepath"
15
"strconv"
···
30
31
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
32
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
33
34
gr, err := git.Open(path, "")
35
if err != nil {
···
37
writeMsg(w, "repo empty")
38
return
39
} else {
40
-
log.Println(err)
41
notFound(w)
42
return
43
}
···
45
commits, err := gr.Commits()
46
if err != nil {
47
writeError(w, err.Error(), http.StatusInternalServerError)
48
-
log.Println(err)
49
return
50
}
51
···
73
}
74
75
if readmeContent == "" {
76
-
log.Printf("no readme found for %s", path)
77
}
78
79
mainBranch, err := gr.FindMainBranch(h.c.Repo.MainBranch)
80
if err != nil {
81
writeError(w, err.Error(), http.StatusInternalServerError)
82
-
log.Println(err)
83
return
84
}
85
···
100
treePath := chi.URLParam(r, "*")
101
ref := chi.URLParam(r, "ref")
102
103
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
104
gr, err := git.Open(path, ref)
105
if err != nil {
···
110
files, err := gr.FileTree(treePath)
111
if err != nil {
112
writeError(w, err.Error(), http.StatusInternalServerError)
113
-
log.Println(err)
114
return
115
}
116
···
133
treePath := chi.URLParam(r, "*")
134
ref := chi.URLParam(r, "ref")
135
136
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
137
gr, err := git.Open(path, ref)
138
if err != nil {
···
155
if raw {
156
h.showRaw(string(safe), w)
157
} else {
158
-
h.showFile(string(safe), data, w)
159
}
160
}
161
162
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
163
name := chi.URLParam(r, "name")
164
file := chi.URLParam(r, "file")
165
166
// TODO: extend this to add more files compression (e.g.: xz)
167
if !strings.HasSuffix(file, ".tar.gz") {
···
192
if err != nil {
193
// once we start writing to the body we can't report error anymore
194
// so we are only left with printing the error.
195
-
log.Println(err)
196
return
197
}
198
···
200
if err != nil {
201
// once we start writing to the body we can't report error anymore
202
// so we are only left with printing the error.
203
-
log.Println(err)
204
return
205
}
206
}
207
208
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
209
-
fmt.Println(r.URL.Path)
210
ref := chi.URLParam(r, "ref")
211
212
-
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
213
gr, err := git.Open(path, ref)
214
if err != nil {
215
notFound(w)
···
219
commits, err := gr.Commits()
220
if err != nil {
221
writeError(w, err.Error(), http.StatusInternalServerError)
222
-
log.Println(err)
223
return
224
}
225
···
269
func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
270
ref := chi.URLParam(r, "ref")
271
272
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
273
gr, err := git.Open(path, ref)
274
if err != nil {
···
279
diff, err := gr.Diff()
280
if err != nil {
281
writeError(w, err.Error(), http.StatusInternalServerError)
282
-
log.Println(err)
283
return
284
}
285
···
297
298
func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) {
299
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
300
gr, err := git.Open(path, "")
301
if err != nil {
302
notFound(w)
···
306
tags, err := gr.Tags()
307
if err != nil {
308
// Non-fatal, we *should* have at least one branch to show.
309
-
log.Println(err)
310
}
311
312
branches, err := gr.Branches()
313
if err != nil {
314
-
log.Println(err)
315
writeError(w, err.Error(), http.StatusInternalServerError)
316
return
317
}
···
327
}
328
329
func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
330
switch r.Method {
331
case http.MethodGet:
332
keys, err := h.db.GetAllPublicKeys()
333
if err != nil {
334
writeError(w, err.Error(), http.StatusInternalServerError)
335
-
log.Println(err)
336
return
337
}
338
···
358
359
if err := h.db.AddPublicKey(pk); err != nil {
360
writeError(w, err.Error(), http.StatusInternalServerError)
361
-
log.Printf("adding public key: %s", err)
362
return
363
}
364
···
368
}
369
370
func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
371
data := struct {
372
Did string `json:"did"`
373
Name string `json:"name"`
···
385
repoPath := filepath.Join(h.c.Repo.ScanPath, relativeRepoPath)
386
err := git.InitBare(repoPath)
387
if err != nil {
388
-
log.Println(err)
389
writeError(w, err.Error(), http.StatusInternalServerError)
390
return
391
}
···
393
// add perms for this user to access the repo
394
err = h.e.AddRepo(did, ThisServer, relativeRepoPath)
395
if err != nil {
396
-
log.Println(err)
397
writeError(w, err.Error(), http.StatusInternalServerError)
398
return
399
}
···
402
}
403
404
func (h *Handle) AddMember(w http.ResponseWriter, r *http.Request) {
405
data := struct {
406
Did string `json:"did"`
407
PublicKeys []string `json:"keys"`
···
427
428
h.js.UpdateDids([]string{did})
429
if err := h.e.AddMember(ThisServer, did); err != nil {
430
-
log.Println(err)
431
writeError(w, err.Error(), http.StatusInternalServerError)
432
return
433
}
···
436
}
437
438
func (h *Handle) Init(w http.ResponseWriter, r *http.Request) {
439
if h.knotInitialized {
440
writeError(w, "knot already initialized", http.StatusConflict)
441
return
···
447
}{}
448
449
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
450
writeError(w, "invalid request body", http.StatusBadRequest)
451
return
452
}
453
454
if data.Did == "" {
455
writeError(w, "did is empty", http.StatusBadRequest)
456
return
457
}
···
464
pk.Key = k
465
err := h.db.AddPublicKey(pk)
466
if err != nil {
467
writeError(w, err.Error(), http.StatusInternalServerError)
468
return
469
}
470
}
471
} else {
472
writeError(w, err.Error(), http.StatusInternalServerError)
473
return
474
}
475
476
h.js.UpdateDids([]string{data.Did})
477
if err := h.e.AddOwner(ThisServer, data.Did); err != nil {
478
-
log.Println(err)
479
writeError(w, err.Error(), http.StatusInternalServerError)
480
return
481
}
482
-
// Signal that the knot is ready
483
close(h.init)
484
485
mac := hmac.New(sha256.New, []byte(h.c.Server.Secret))
···
9
"errors"
10
"fmt"
11
"html/template"
12
"net/http"
13
"path/filepath"
14
"strconv"
···
29
30
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
31
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
32
+
l := h.l.With("path", path, "handler", "RepoIndex")
33
34
gr, err := git.Open(path, "")
35
if err != nil {
···
37
writeMsg(w, "repo empty")
38
return
39
} else {
40
+
l.Error("opening repo", "error", err.Error())
41
notFound(w)
42
return
43
}
···
45
commits, err := gr.Commits()
46
if err != nil {
47
writeError(w, err.Error(), http.StatusInternalServerError)
48
+
l.Error("fetching commits", "error", err.Error())
49
return
50
}
51
···
73
}
74
75
if readmeContent == "" {
76
+
l.Warn("no readme found")
77
}
78
79
mainBranch, err := gr.FindMainBranch(h.c.Repo.MainBranch)
80
if err != nil {
81
writeError(w, err.Error(), http.StatusInternalServerError)
82
+
l.Error("finding main branch", "error", err.Error())
83
return
84
}
85
···
100
treePath := chi.URLParam(r, "*")
101
ref := chi.URLParam(r, "ref")
102
103
+
l := h.l.With("handler", "RepoTree", "ref", ref, "treePath", treePath)
104
+
105
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
106
gr, err := git.Open(path, ref)
107
if err != nil {
···
112
files, err := gr.FileTree(treePath)
113
if err != nil {
114
writeError(w, err.Error(), http.StatusInternalServerError)
115
+
l.Error("file tree", "error", err.Error())
116
return
117
}
118
···
135
treePath := chi.URLParam(r, "*")
136
ref := chi.URLParam(r, "ref")
137
138
+
l := h.l.With("handler", "FileContent", "ref", ref, "treePath", treePath)
139
+
140
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
141
gr, err := git.Open(path, ref)
142
if err != nil {
···
159
if raw {
160
h.showRaw(string(safe), w)
161
} else {
162
+
h.showFile(string(safe), data, w, l)
163
}
164
}
165
166
func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
167
name := chi.URLParam(r, "name")
168
file := chi.URLParam(r, "file")
169
+
170
+
l := h.l.With("handler", "Archive", "name", name, "file", file)
171
172
// TODO: extend this to add more files compression (e.g.: xz)
173
if !strings.HasSuffix(file, ".tar.gz") {
···
198
if err != nil {
199
// once we start writing to the body we can't report error anymore
200
// so we are only left with printing the error.
201
+
l.Error("writing tar file", "error", err.Error())
202
return
203
}
204
···
206
if err != nil {
207
// once we start writing to the body we can't report error anymore
208
// so we are only left with printing the error.
209
+
l.Error("flushing?", "error", err.Error())
210
return
211
}
212
}
213
214
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
215
ref := chi.URLParam(r, "ref")
216
+
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
217
218
+
l := h.l.With("handler", "Log", "ref", ref, "path", path)
219
+
220
gr, err := git.Open(path, ref)
221
if err != nil {
222
notFound(w)
···
226
commits, err := gr.Commits()
227
if err != nil {
228
writeError(w, err.Error(), http.StatusInternalServerError)
229
+
l.Error("fetching commits", "error", err.Error())
230
return
231
}
232
···
276
func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
277
ref := chi.URLParam(r, "ref")
278
279
+
l := h.l.With("handler", "Diff", "ref", ref)
280
+
281
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
282
gr, err := git.Open(path, ref)
283
if err != nil {
···
288
diff, err := gr.Diff()
289
if err != nil {
290
writeError(w, err.Error(), http.StatusInternalServerError)
291
+
l.Error("getting diff", "error", err.Error())
292
return
293
}
294
···
306
307
func (h *Handle) Refs(w http.ResponseWriter, r *http.Request) {
308
path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
309
+
l := h.l.With("handler", "Refs")
310
+
311
gr, err := git.Open(path, "")
312
if err != nil {
313
notFound(w)
···
317
tags, err := gr.Tags()
318
if err != nil {
319
// Non-fatal, we *should* have at least one branch to show.
320
+
l.Error("getting tags", "error", err.Error())
321
}
322
323
branches, err := gr.Branches()
324
if err != nil {
325
+
l.Error("getting branches", "error", err.Error())
326
writeError(w, err.Error(), http.StatusInternalServerError)
327
return
328
}
···
338
}
339
340
func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) {
341
+
l := h.l.With("handler", "Keys")
342
+
343
switch r.Method {
344
case http.MethodGet:
345
keys, err := h.db.GetAllPublicKeys()
346
if err != nil {
347
writeError(w, err.Error(), http.StatusInternalServerError)
348
+
l.Error("getting public keys", "error", err.Error())
349
return
350
}
351
···
371
372
if err := h.db.AddPublicKey(pk); err != nil {
373
writeError(w, err.Error(), http.StatusInternalServerError)
374
+
l.Error("adding public key", "error", err.Error())
375
return
376
}
377
···
381
}
382
383
func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
384
+
l := h.l.With("handler", "NewRepo")
385
+
386
data := struct {
387
Did string `json:"did"`
388
Name string `json:"name"`
···
400
repoPath := filepath.Join(h.c.Repo.ScanPath, relativeRepoPath)
401
err := git.InitBare(repoPath)
402
if err != nil {
403
+
l.Error("initializing bare repo", "error", err.Error())
404
writeError(w, err.Error(), http.StatusInternalServerError)
405
return
406
}
···
408
// add perms for this user to access the repo
409
err = h.e.AddRepo(did, ThisServer, relativeRepoPath)
410
if err != nil {
411
+
l.Error("adding repo permissions", "error", err.Error())
412
writeError(w, err.Error(), http.StatusInternalServerError)
413
return
414
}
···
417
}
418
419
func (h *Handle) AddMember(w http.ResponseWriter, r *http.Request) {
420
+
l := h.l.With("handler", "AddMember")
421
+
422
data := struct {
423
Did string `json:"did"`
424
PublicKeys []string `json:"keys"`
···
444
445
h.js.UpdateDids([]string{did})
446
if err := h.e.AddMember(ThisServer, did); err != nil {
447
+
l.Error("adding member", "error", err.Error())
448
writeError(w, err.Error(), http.StatusInternalServerError)
449
return
450
}
···
453
}
454
455
func (h *Handle) Init(w http.ResponseWriter, r *http.Request) {
456
+
l := h.l.With("handler", "Init")
457
+
458
if h.knotInitialized {
459
writeError(w, "knot already initialized", http.StatusConflict)
460
return
···
466
}{}
467
468
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
469
+
l.Error("failed to decode request body", "error", err.Error())
470
writeError(w, "invalid request body", http.StatusBadRequest)
471
return
472
}
473
474
if data.Did == "" {
475
+
l.Error("empty DID in request")
476
writeError(w, "did is empty", http.StatusBadRequest)
477
return
478
}
···
485
pk.Key = k
486
err := h.db.AddPublicKey(pk)
487
if err != nil {
488
+
l.Error("failed to add public key", "error", err.Error())
489
writeError(w, err.Error(), http.StatusInternalServerError)
490
return
491
}
492
}
493
} else {
494
+
l.Error("failed to add DID", "error", err.Error())
495
writeError(w, err.Error(), http.StatusInternalServerError)
496
return
497
}
498
499
h.js.UpdateDids([]string{data.Did})
500
if err := h.e.AddOwner(ThisServer, data.Did); err != nil {
501
+
l.Error("adding owner", "error", err.Error())
502
writeError(w, err.Error(), http.StatusInternalServerError)
503
return
504
}
505
+
506
close(h.init)
507
508
mac := hmac.New(sha256.New, []byte(h.c.Server.Secret))
+49
log/log.go
+49
log/log.go
···
···
1
+
package log
2
+
3
+
import (
4
+
"context"
5
+
"log/slog"
6
+
"os"
7
+
)
8
+
9
+
// NewHandler sets up a new slog.Handler with the service name
10
+
// as an attribute
11
+
func NewHandler(name string) slog.Handler {
12
+
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{})
13
+
14
+
var attrs []slog.Attr
15
+
attrs = append(attrs, slog.Attr{Key: "service", Value: slog.StringValue(name)})
16
+
handler.WithAttrs(attrs)
17
+
return handler
18
+
}
19
+
20
+
func New(name string) *slog.Logger {
21
+
return slog.New(NewHandler(name))
22
+
}
23
+
24
+
func NewContext(ctx context.Context, name string) context.Context {
25
+
return IntoContext(ctx, New(name))
26
+
}
27
+
28
+
type ctxKey struct{}
29
+
30
+
// IntoContext adds a logger to a context. Use FromContext to
31
+
// pull the logger out.
32
+
func IntoContext(ctx context.Context, l *slog.Logger) context.Context {
33
+
return context.WithValue(ctx, ctxKey{}, l)
34
+
}
35
+
36
+
// FromContext returns a logger from a context.Context;
37
+
// if the passed context is nil, we return the default slog
38
+
// logger.
39
+
func FromContext(ctx context.Context) *slog.Logger {
40
+
if ctx != nil {
41
+
v := ctx.Value(ctxKey{})
42
+
if v == nil {
43
+
return slog.Default()
44
+
}
45
+
return v.(*slog.Logger)
46
+
}
47
+
48
+
return slog.Default()
49
+
}