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) }()
}