package main import ( "context" "log/slog" "time" work "git.sr.ht/~sircmpwn/dowork" sq "github.com/Masterminds/squirrel" "github.com/google/uuid" "sourcecraft.dev/bigbes/sr-ht-core/auth" "sourcecraft.dev/bigbes/sr-ht-core/webhooks" "sourcecraft.dev/bigbes/sr-ht-spec/graph" "sourcecraft.dev/bigbes/sr-ht-spec/graph/model" "sourcecraft.dev/bigbes/sr-ht-spec/service" ) // webhookEventSink implements service.EventSink by enqueuing a dowork task onto // the webhook queue. The task runs in the queue's worker context (server + // database + config, from WithQueues), adds the owner's INTERNAL auth context, // and calls Schedule — which needs all three. Firing is fire-and-forget: a // webhook must never block or fail a proposal write. type webhookEventSink struct { queue *webhooks.WebhookQueue ownerUserID int ownerName string log *slog.Logger } func newWebhookEventSink(q *webhooks.WebhookQueue, ownerUserID int, ownerName string, log *slog.Logger) *webhookEventSink { return &webhookEventSink{queue: q, ownerUserID: ownerUserID, ownerName: ownerName, log: log} } // Compile-time assertion that the sink satisfies the service seam. var _ service.EventSink = (*webhookEventSink)(nil) func (s *webhookEventSink) ProposalEvent(kind service.ProposalEventKind, p service.Proposal) { u := uuid.New() payload, err := graph.NewProposalEvent(model.WebhookEvent(kind), u.String(), time.Now().UTC(), p) if err != nil { s.log.Error("build webhook payload", "err", err) return } event := string(kind) task := work.NewTask(func(ctx context.Context) error { // The worker context carries server+database+config; add the owner auth // Schedule captures (fetchSubscriptions and the delivery both need it). ctx = auth.Context(ctx, &auth.AuthContext{ AuthMethod: auth.AUTH_INTERNAL, UserID: s.ownerUserID, Username: s.ownerName, }) q := sq.Select().From("gql_user_wh_sub sub").Where("sub.user_id = ?", s.ownerUserID) s.queue.Schedule(ctx, q, "user", event, u, payload) return nil }) // Enqueue off the write path so a full queue never blocks a merge. go func() { _ = s.queue.Queue.Enqueue(task) }() }