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