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