feat: Function triggers — schedule, inbound hook, domain events

Migration 00016: function_id on scheduled_jobs/inbound_hooks,
event_pattern on functions, 'event' in the run-trigger enum.

- Jobs deliver to a function instead of HTTP when function_id is set;
  the run's status_code mirrors the process exit code. Clearing the
  field returns the job to URL delivery.
- Inbound hooks gain mode "function" — received body becomes stdin
  payload, acked immediately, run recorded async.
- event_pattern (document.*, *, exact type) fires the function detached
  on matching publishEvent — never blocks realtime/webhook fan-out.
- One executeFunction path serves manual/schedule/hook/event; run rows
  record the trigger source.
- Backend image now ships bun 1.3.14 so Functions work under compose.

Dashboard: job target picker, hook function mode, event-pattern field.

Verified live: job run → success (status_code 0), hook POST → run,
document.created → event run with correct payload.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-24 20:28:04 +02:00
1 parent c094afad65
commit a84049b8fc
40 files changed
+716 -157

No files matched your search

@@ -40,6 +40,7 @@ type PlatformService struct {
scheduler *JobScheduler
alerts *alertEvaluator
functions FunctionRunner
logger *slog.Logger
}
type BootstrapInput struct {
@@ -226,11 +227,12 @@ type InvitationSummary struct {
}
func NewPlatformService(repo *repositories.CoreRepository, store storage.Store, mailer *Mailer, publicURL string, dbxClient *dbx.Client, enc *secrets.Encryptor, logger *slog.Logger, functions FunctionRunner) *PlatformService {
s := &PlatformService{repo: repo, store: store, mailer: mailer, publicURL: publicURL, dbx: dbxClient, hub: NewEventHub(), realtime: NewEventHub(), enc: enc, functions: functions}
s := &PlatformService{repo: repo, store: store, mailer: mailer, publicURL: publicURL, dbx: dbxClient, hub: NewEventHub(), realtime: NewEventHub(), enc: enc, functions: functions, logger: logger}
if enc != nil {
s.dispatcher = NewWebhookDispatcher(repo.Queries(), enc, logger)
s.dispatcher.Start(context.Background())
s.scheduler = NewJobScheduler(repo.Queries(), enc, logger, s.publishEvent, repo.Pool())
s.scheduler.runFn = s.runJobFunction
s.scheduler.Start(context.Background())
s.alerts = newAlertEvaluator(repo.Queries(), s.publishEvent, logger, repo.Pool())
s.alerts.Start(context.Background())
@@ -327,6 +329,7 @@ func (s *PlatformService) publishEvent(ctx context.Context, projectID uuid.UUID,
})
}
s.dispatchEvent(ctx, projectID, eventType, data)
s.triggerFunctionsForEvent(ctx, projectID, eventType, data)
}
// SubscribeRealtime registers an SSE consumer for project domain events.