forked from tangled.org/core
this repo has no description
at master 3.1 kB view raw
1package xrpc 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "path/filepath" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 securejoin "github.com/cyphar/filepath-securejoin" 11 "tangled.sh/tangled.sh/core/api/tangled" 12 "tangled.sh/tangled.sh/core/knotserver/git" 13 "tangled.sh/tangled.sh/core/rbac" 14 "tangled.sh/tangled.sh/core/types" 15 xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 16) 17 18func (x *Xrpc) ForkStatus(w http.ResponseWriter, r *http.Request) { 19 l := x.Logger.With("handler", "ForkStatus") 20 fail := func(e xrpcerr.XrpcError) { 21 l.Error("failed", "kind", e.Tag, "error", e.Message) 22 writeError(w, e, http.StatusBadRequest) 23 } 24 25 actorDid, ok := r.Context().Value(ActorDid).(syntax.DID) 26 if !ok { 27 fail(xrpcerr.MissingActorDidError) 28 return 29 } 30 31 var data tangled.RepoForkStatus_Input 32 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 33 fail(xrpcerr.GenericError(err)) 34 return 35 } 36 37 did := data.Did 38 source := data.Source 39 branch := data.Branch 40 hiddenRef := data.HiddenRef 41 42 if did == "" || source == "" || branch == "" || hiddenRef == "" { 43 fail(xrpcerr.GenericError(fmt.Errorf("did, source, branch, and hiddenRef are required"))) 44 return 45 } 46 47 var name string 48 if data.Name != "" { 49 name = data.Name 50 } else { 51 name = filepath.Base(source) 52 } 53 54 relativeRepoPath := filepath.Join(did, name) 55 56 if ok, err := x.Enforcer.IsPushAllowed(actorDid.String(), rbac.ThisServer, relativeRepoPath); !ok || err != nil { 57 l.Error("insufficient permissions", "did", actorDid.String(), "repo", relativeRepoPath) 58 writeError(w, xrpcerr.AccessControlError(actorDid.String()), http.StatusUnauthorized) 59 return 60 } 61 62 repoPath, err := securejoin.SecureJoin(x.Config.Repo.ScanPath, relativeRepoPath) 63 if err != nil { 64 fail(xrpcerr.GenericError(err)) 65 return 66 } 67 68 gr, err := git.PlainOpen(repoPath) 69 if err != nil { 70 fail(xrpcerr.GenericError(fmt.Errorf("failed to open repository: %w", err))) 71 return 72 } 73 74 forkCommit, err := gr.ResolveRevision(branch) 75 if err != nil { 76 l.Error("error resolving ref revision", "msg", err.Error()) 77 fail(xrpcerr.GenericError(fmt.Errorf("error resolving revision %s: %w", branch, err))) 78 return 79 } 80 81 sourceCommit, err := gr.ResolveRevision(hiddenRef) 82 if err != nil { 83 l.Error("error resolving hidden ref revision", "msg", err.Error()) 84 fail(xrpcerr.GenericError(fmt.Errorf("error resolving revision %s: %w", hiddenRef, err))) 85 return 86 } 87 88 status := types.UpToDate 89 if forkCommit.Hash.String() != sourceCommit.Hash.String() { 90 isAncestor, err := forkCommit.IsAncestor(sourceCommit) 91 if err != nil { 92 l.Error("error checking ancestor relationship", "error", err.Error()) 93 fail(xrpcerr.GenericError(fmt.Errorf("error resolving whether %s is ancestor of %s: %w", branch, hiddenRef, err))) 94 return 95 } 96 97 if isAncestor { 98 status = types.FastForwardable 99 } else { 100 status = types.Conflict 101 } 102 } 103 104 response := tangled.RepoForkStatus_Output{ 105 Status: int64(status), 106 } 107 108 w.Header().Set("Content-Type", "application/json") 109 w.WriteHeader(http.StatusOK) 110 json.NewEncoder(w).Encode(response) 111}