forked from tangled.org/core
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/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}