This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / spindle / xrpc / xrpc.go
3.4 kB 108 lines
1package xrpc 2 3import ( 4 "context" 5 _ "embed" 6 "encoding/json" 7 "errors" 8 "log/slog" 9 "net/http" 10 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "github.com/go-chi/chi/v5" 13 14 "tangled.org/core/api/tangled" 15 "tangled.org/core/idresolver" 16 "tangled.org/core/notifier" 17 "tangled.org/core/rbac" 18 "tangled.org/core/spindle/config" 19 "tangled.org/core/spindle/db" 20 "tangled.org/core/spindle/models" 21 "tangled.org/core/spindle/secrets" 22 xrpcerr "tangled.org/core/xrpc/errors" 23 "tangled.org/core/xrpc/serviceauth" 24) 25 26const ActorDid = serviceauth.ActorDid 27 28var ErrNoMatchingWorkflows = errors.New("no workflows to run") 29 30// this is to break an import cycle. spindle imports this package for Xrpc, 31// so this package can't import *spindle.Spindle back. 32type PipelineTrigger interface { 33 TriggerManual(ctx context.Context, repoDid syntax.DID, sha, ref string, workflows []string, sourceRepo syntax.DID, pull PullContext, inputs []*tangled.Pipeline_Pair) (syntax.ATURI, error) 34} 35 36type PullContext struct { 37 IsPullRequest bool 38 Pull syntax.ATURI 39 SourceBranch string 40 TargetBranch string 41} 42 43type Xrpc struct { 44 Logger *slog.Logger 45 Db *db.DB 46 Enforcer *rbac.Enforcer 47 Engines map[string]models.Engine 48 Config *config.Config 49 Resolver *idresolver.Resolver 50 Vault secrets.Manager 51 Notifier *notifier.Notifier 52 ServiceAuth *serviceauth.ServiceAuth 53 Trigger PipelineTrigger 54} 55 56func (x *Xrpc) Router() http.Handler { 57 r := chi.NewRouter() 58 r.Use(x.cors) 59 60 r.Group(func(r chi.Router) { 61 r.Use(x.ServiceAuth.VerifyServiceAuth) 62 63 r.Post("/"+tangled.RepoAddSecretNSID, x.AddSecret) 64 r.Post("/"+tangled.RepoRemoveSecretNSID, x.RemoveSecret) 65 r.Get("/"+tangled.RepoListSecretsNSID, x.ListSecrets) 66 r.Post("/"+tangled.CiCancelPipelineNSID, x.CancelPipeline) 67 r.Post("/"+tangled.CiTriggerPipelineNSID, x.TriggerPipeline) 68 }) 69 70 // service query endpoints (no auth required) 71 r.Get("/"+tangled.OwnerNSID, x.Owner) 72 r.Get("/"+tangled.CiSubscribePipelineLogsNSID, x.HandleCiSubscribePipelineLogs) 73 r.Get("/"+tangled.CiQueryPipelinesNSID, x.HandleCiQueryPipelines) 74 r.Get("/"+tangled.CiGetPipelineNSID, x.HandleCiGetPipeline) 75 76 return r 77} 78 79// cors allows the browser origin to call these xrpc endpoints directly. auth is 80// via service-auth bearer tokens, not cookies, so a wildcard origin is safe. 81func (x *Xrpc) cors(next http.Handler) http.Handler { 82 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 83 w.Header().Set("Access-Control-Allow-Origin", "*") 84 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") 85 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") 86 w.Header().Set("Access-Control-Max-Age", "86400") 87 if r.Method == http.MethodOptions { 88 w.WriteHeader(http.StatusNoContent) 89 return 90 } 91 next.ServeHTTP(w, r) 92 }) 93} 94 95// this is slightly different from http_util::write_error to follow the spec: 96// 97// the json object returned must include an "error" and a "message" 98func writeError(w http.ResponseWriter, e xrpcerr.XrpcError, status int) { 99 w.Header().Set("Content-Type", "application/json") 100 w.WriteHeader(status) 101 json.NewEncoder(w).Encode(e) 102} 103 104func writeJson(w http.ResponseWriter, status int, response any) error { 105 w.Header().Set("Content-Type", "application/json") 106 w.WriteHeader(status) 107 return json.NewEncoder(w).Encode(response) 108}