Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166package xrpc
import ( "context" _ "embed" "encoding/json" "errors" "fmt" "log/slog" "net/http"
"github.com/bluesky-social/indigo/atproto/syntax" "github.com/go-chi/chi/v5" "github.com/go-chi/cors"
"tangled.org/core/api/org_tangled" "tangled.org/core/api/tangled" "tangled.org/core/idresolver" "tangled.org/core/notifier" "tangled.org/core/rbac" "tangled.org/core/spindle/artifactstore" "tangled.org/core/spindle/config" "tangled.org/core/spindle/db" "tangled.org/core/spindle/models" "tangled.org/core/spindle/quota" "tangled.org/core/spindle/secrets" "tangled.org/core/spindle/webhook" xrpcerr "tangled.org/core/xrpc/errors" "tangled.org/core/xrpc/serviceauth")
const ActorDid = serviceauth.ActorDid
var ErrNoMatchingWorkflows = errors.New("no workflows to run")
func requireSha(sha string) error { if len(sha) != 40 { return fmt.Errorf("sha must be a 40-character commit hash") } return nil}
// this is to break an import cycle. spindle imports this package for Xrpc,// so this package can't import *spindle.Spindle back.type PipelineTrigger interface { TriggerManual(ctx context.Context, actor, repoDid syntax.DID, sha, ref string, workflows []string, sourceRepo syntax.DID, pull PullContext, inputs []*tangled.Pipeline_Pair) (syntax.ATURI, error) DescribeWorkflowDefinition(ctx context.Context, repoDid syntax.DID, sha string, sourceRepo syntax.DID) (*tangled.CiDescribeWorkflowDefinition_Output, error) ListWorkflowDefinitions(ctx context.Context, repoDid syntax.DID, ref string) ([]*models.WorkflowDefinition, error)}
type RepoWiper interface { WipeRepo(ctx context.Context, repoDid syntax.DID, reason string) error WipeOwner(ctx context.Context, ownerDid syntax.DID, reason string) error}
type PullContext struct { IsPullRequest bool Pull syntax.ATURI SourceBranch string TargetBranch string}
type Xrpc struct { Logger *slog.Logger Db *db.DB Enforcer *rbac.Enforcer Engines map[string]models.Engine Config *config.Config ArtifactReader artifactstore.Reader Resolver *idresolver.Resolver Vault secrets.Manager Notifier *notifier.Notifier Webhooks *webhook.Service ServiceAuth *serviceauth.ServiceAuth Trigger PipelineTrigger QuotaStore quota.Store Wiper RepoWiper Backfill OwnerBackfill}
func (x *Xrpc) Router() http.Handler { r := chi.NewRouter()
// browsers preflight anything carrying a service auth header, and without an // answer they drop the real request before it leaves if x.Config != nil && len(x.Config.Server.CorsOrigins) > 0 { r.Use(cors.Handler(cors.Options{ AllowedOrigins: x.Config.Server.CorsOrigins, AllowedMethods: []string{http.MethodGet, http.MethodPost}, AllowedHeaders: []string{"Authorization", "Content-Type"}, MaxAge: 86400, })) }
r.Get("/"+org_tangled.CiGetWorkflowDefinitionNSID, x.handleOrgTangledCiGetWorkflowDefinition) r.Get("/"+org_tangled.CiListWorkflowDefinitionsNSID, x.handleOrgTangledCiListWorkflowDefinitions) r.Get("/"+org_tangled.CiSubscribePipelineLogsNSID, x.handleOrgTangledCiSubscribePipelineLogs) r.Get("/"+org_tangled.CiQueryPipelinesNSID, x.handleOrgTangledCiQueryPipelines) r.Get("/"+org_tangled.CiGetPipelineNSID, x.handleOrgTangledCiGetPipeline)
// authenticated endpoints r.Group(func(r chi.Router) { r.Use(x.ServiceAuth.VerifyServiceAuth)
r.Post("/"+org_tangled.SecretAddSecretNSID, x.handleOrgTangledSecretAddSecret) r.Post("/"+org_tangled.SecretRemoveSecretNSID, x.handleOrgTangledSecretRemoveSecret) r.Get("/"+org_tangled.SecretListSecretsNSID, x.handleOrgTangledSecretListSecrets) r.Post("/"+tangled.CiCancelPipelineNSID, x.CancelPipeline) r.Post("/"+tangled.CiTriggerPipelineNSID, x.TriggerPipeline) r.Post("/"+org_tangled.TempSpindleQuotaSetNSID, x.SetLimit) r.Post("/"+org_tangled.TempSpindleQuotaUnsetNSID, x.UnsetLimit) r.Get("/"+org_tangled.TempSpindleQuotaGetNSID, x.GetLimit) r.Get("/"+org_tangled.TempSpindleQuotaListNSID, x.ListLimits) r.Get("/"+org_tangled.TempSpindleQuotaUsageNSID, x.GetUsage) r.Post("/"+org_tangled.TempSpindleModerationBanNSID, x.Ban) r.Post("/"+org_tangled.TempSpindleModerationUnbanNSID, x.Unban) r.Get("/"+org_tangled.TempSpindleModerationGetBanNSID, x.GetBan) r.Get("/"+org_tangled.TempSpindleModerationListBansNSID, x.ListBans) r.Post("/"+org_tangled.TempSpindleAllowListAddNSID, x.AllowListAdd) r.Post("/"+org_tangled.TempSpindleAllowListRemoveNSID, x.AllowListRemove) r.Get("/"+org_tangled.TempSpindleAllowListListNSID, x.AllowListList)
r.Post("/"+org_tangled.TempWebhookCreateWebhookNSID, x.WebhookCreate) r.Post("/"+org_tangled.TempWebhookUpdateWebhookNSID, x.WebhookUpdate) r.Post("/"+org_tangled.TempWebhookDeleteWebhookNSID, x.WebhookDelete) r.Post("/"+org_tangled.TempWebhookToggleWebhookNSID, x.WebhookToggle) r.Post("/"+org_tangled.TempWebhookRetryDeliveryNSID, x.WebhookRetryDelivery) r.Get("/"+org_tangled.TempWebhookListWebhooksNSID, x.WebhookList) r.Get("/"+org_tangled.TempWebhookGetDeliveriesForWebhooksNSID, x.WebhookGetDeliveriesForWebhooks) })
// service query endpoints (no auth required) r.Get("/"+tangled.OwnerNSID, x.Owner) r.Get("/"+tangled.CiDescribeWorkflowDefinitionNSID, x.DescribeWorkflowDefinition) r.Get("/"+tangled.CiSubscribePipelineLogsNSID, x.HandleCiSubscribePipelineLogs) r.Get("/"+tangled.CiQueryPipelinesNSID, x.HandleCiQueryPipelines) r.Get("/"+tangled.CiGetPipelineNSID, x.HandleCiGetPipeline)
return r}
func invalidRequest(err error) xrpcerr.XrpcError { return xrpcerr.NewXrpcError(xrpcerr.WithTag("InvalidRequest"), xrpcerr.WithError(err))}
// this is slightly different from http_util::write_error to follow the spec://// the json object returned must include an "error" and a "message"func writeError(w http.ResponseWriter, e xrpcerr.XrpcError, status int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(e)}
func writeJson(w http.ResponseWriter, status int, response any) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) return json.NewEncoder(w).Encode(response)}
func (x *Xrpc) writeResponse(w http.ResponseWriter, r *http.Request, status int, response any) { if err := writeJson(w, status, response); err != nil { x.Logger.ErrorContext(r.Context(), "failed to write response", "err", err) }}