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