this repo has no description
1package knotserver
2
3import (
4 "bufio"
5 "context"
6 "log/slog"
7 "net/http"
8 "path/filepath"
9 "strings"
10
11 "github.com/go-chi/chi/v5"
12 "github.com/go-chi/chi/v5/middleware"
13 "tangled.sh/tangled.sh/core/knotserver/config"
14 "tangled.sh/tangled.sh/core/knotserver/db"
15 "tangled.sh/tangled.sh/core/rbac"
16)
17
18type InternalHandle struct {
19 db *db.DB
20 c *config.Config
21 e *rbac.Enforcer
22 l *slog.Logger
23}
24
25func (h *InternalHandle) PushAllowed(w http.ResponseWriter, r *http.Request) {
26 user := r.URL.Query().Get("user")
27 repo := r.URL.Query().Get("repo")
28
29 if user == "" || repo == "" {
30 w.WriteHeader(http.StatusBadRequest)
31 return
32 }
33
34 ok, err := h.e.IsPushAllowed(user, ThisServer, repo)
35 if err != nil || !ok {
36 w.WriteHeader(http.StatusForbidden)
37 return
38 }
39
40 w.WriteHeader(http.StatusNoContent)
41 return
42}
43
44func (h *InternalHandle) InternalKeys(w http.ResponseWriter, r *http.Request) {
45 keys, err := h.db.GetAllPublicKeys()
46 if err != nil {
47 writeError(w, err.Error(), http.StatusInternalServerError)
48 return
49 }
50
51 data := make([]map[string]interface{}, 0)
52 for _, key := range keys {
53 j := key.JSON()
54 data = append(data, j)
55 }
56 writeJSON(w, data)
57 return
58}
59
60func (h *InternalHandle) PostReceiveHook(w http.ResponseWriter, r *http.Request) {
61 l := h.l.With("handler", "PostReceiveHook")
62
63 gitAbsoluteDir := r.Header.Get("X-Git-Dir")
64 gitRelativeDir, err := filepath.Rel(h.c.Repo.ScanPath, gitAbsoluteDir)
65 if err != nil {
66 l.Error("failed to calculate relative git dir", "scanPath", h.c.Repo.ScanPath, "gitAbsoluteDir", gitAbsoluteDir)
67 return
68 }
69 gitUserDid := r.Header.Get("X-Git-User-Did")
70
71 var ops []db.Op
72 scanner := bufio.NewScanner(r.Body)
73 for scanner.Scan() {
74 line := scanner.Text()
75 parts := strings.SplitN(line, " ", 3)
76 if len(parts) != 3 {
77 l.Error("invalid payload", "parts", parts)
78 continue
79 }
80
81 tid := TID()
82 oldSha := parts[0]
83 newSha := parts[1]
84 ref := parts[2]
85 op := db.Op{
86 Tid: tid,
87 Did: gitUserDid,
88 Repo: gitRelativeDir,
89 OldSha: oldSha,
90 NewSha: newSha,
91 Ref: ref,
92 }
93 ops = append(ops, op)
94 }
95
96 if err := scanner.Err(); err != nil {
97 l.Error("failed to read payload", "err", err)
98 return
99 }
100
101 for _, op := range ops {
102 err := h.db.InsertOp(op)
103 if err != nil {
104 l.Error("failed to insert op", "err", err, "op", op)
105 continue
106 }
107 }
108
109 return
110}
111
112func Internal(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, l *slog.Logger) http.Handler {
113 r := chi.NewRouter()
114
115 h := InternalHandle{
116 db,
117 c,
118 e,
119 l,
120 }
121
122 r.Get("/push-allowed", h.PushAllowed)
123 r.Get("/keys", h.InternalKeys)
124 r.Post("/hooks/post-receive", h.PostReceiveHook)
125 r.Mount("/debug", middleware.Profiler())
126
127 return r
128}