mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 15:51:23 +00:00
feat: phase 7 automation & realtime — scheduled jobs, domain events, SSE
Scheduled jobs (migration 00009): cron expressions + @every descriptors, in-process scheduler (15s scan, missed-run collapse), signed HTTP POST delivery (X-Primora-Signature), AES-256-GCM sealed secrets returned once, run history with status/duration/http-code, manual execution, retention pruning. Viewer reads, admin/developer writes. Domain events: publishEvent fans document/object/issue/deploy/job mutations to realtime subscribers and matching webhooks through one path. Webhook event filter extended to job.run, document.*, object.*. Realtime: GET /projects/:id/realtime/stream SSE with 25s keepalives and ?token=/?api_key= auth for EventSource clients. Automation dashboard page: Schedules tab (CRUD, run history, manual run, secret-once banner) + Live events tab (live badge, pause/clear). Demo mode parity. primora jobs:* CLI group, six primora_*_job* MCP tools, AutomationService in the generated client. Fixes folded in: gzip middleware buffered SSE flushes (broke EventSource on both streams — */stream now bypasses compression); event payloads double-marshaled json.RawMessage as base64; compose hardcoded localhost trusted origins so non-default ports broke sign-in; frontend auth base URL baked an absolute localhost instead of resolving /auth on origin. Verified live: stack up on :8085, signed delivery HMAC-checked at sink, @every 30s schedule firing, run history rendering, document.created landing in the live events feed. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
4b7fdf35ad
commit
9e9a3d5f98
49 files changed
+3555
-32
No files matched your search
+73
-3
@@ -219,12 +219,82 @@ release pipeline with images + binaries + desktop bundles.
|
||||
smoke green in CI, and the upgrade runbook followed once on a real
|
||||
upgrade. Docs site revisited here, not before.
|
||||
|
||||
## Phase 7 — Automation & realtime (v0.7.0) ✅
|
||||
|
||||
The last Supabase/Appwrite-parity gaps: scheduled work and a live view of
|
||||
what the platform is doing. One event fan-out drives webhooks and the
|
||||
dashboard's live feed off the same stream.
|
||||
|
||||
- **Scheduled jobs** — `core.scheduled_jobs` + `core.scheduled_job_runs`
|
||||
(migration 00009). Cron expressions and descriptors (`@hourly`,
|
||||
`@daily`, `@every 30m`, `*/15 * * * *`) via robfig/cron. In-process
|
||||
`JobScheduler`: 15 s due-scan, buffered execution queue, missed
|
||||
schedules collapse into one run, run history pruned by retention.
|
||||
- **Signed delivery** — jobs POST a JSON envelope (event, project/job/run
|
||||
IDs, trigger, timestamp, static payload) with
|
||||
`X-Primora-Signature: sha256=<hmac-sha256(body)>`, 10 s timeout. Secrets
|
||||
are AES-256-GCM sealed at rest and the generated value is returned
|
||||
exactly once (`has_secret` thereafter).
|
||||
- **Run history** — status (running/success/failed), trigger source,
|
||||
HTTP status, duration, error text, timestamps; `POST …/jobs/:id/run`
|
||||
for manual execution. Role-gated: viewers read, admin/developer write.
|
||||
- **Domain events** — `publishEvent` fans document/object/issue/deploy/
|
||||
job mutations out to realtime subscribers and matching webhooks.
|
||||
Webhook event filter extended to `job.run`, `document.*`, `object.*`.
|
||||
- **Realtime stream** — `GET …/projects/:projectID/realtime/stream` SSE,
|
||||
25 s keepalives, `?token=`/`?api_key=` query auth for `EventSource`.
|
||||
- **Automation page** — Schedules tab (table, create modal, run history
|
||||
expansion, manual run, enable/disable, delete, secret-shown-once) and
|
||||
Live events tab (live badge, pause, clear, reconnect). Demo-mode seeds
|
||||
and stubs included; command palette entry added.
|
||||
- **Surfaces** — `primora jobs:*` CLI group (list/create/run/runs/rm),
|
||||
six new `primora_*_job*` MCP tools, `AutomationService` in the
|
||||
generated client.
|
||||
|
||||
### Fixes folded in
|
||||
|
||||
- **gzip swallowed SSE** — the compression middleware buffered every
|
||||
flush, so `EventSource` saw headers then nothing and died. `*/stream`
|
||||
paths now bypass compression (telemetry stream was equally broken).
|
||||
- **Trusted origins ignored `PUBLIC_URL`** — compose hardcoded
|
||||
`http://localhost` for `BETTER_AUTH_URL`/`AUTH_BASE_URL`/`VITE_APP_URL`;
|
||||
they now read env so a non-default port/domain actually works.
|
||||
- **Relative auth base URL** — better-auth rejects relative URLs;
|
||||
`auth-client` now resolves `/auth` against `window.location.origin`,
|
||||
preserving same-origin defaults without breaking split-origin dev.
|
||||
|
||||
## Future phases — candidates
|
||||
|
||||
Ordered loosely by leverage. None committed; each gets scoped when picked.
|
||||
|
||||
- **Phase 8 — Client realtime SDK** — `@primora/client` channel
|
||||
subscriptions (`channel('documents').on('created', …)`), presence,
|
||||
and broadcast over the Phase 7 stream. Supabase-realtime parity is a
|
||||
protocol question, not a transport one.
|
||||
- **Phase 9 — Functions** — user code on schedule/event/HTTP triggers.
|
||||
Deno or Bun isolates in a sidecar; secrets via env, payload via stdin.
|
||||
The job runner already supplies the scheduling half.
|
||||
- **Phase 10 — Storage backends** — S3-compatible object storage behind
|
||||
the existing bucket abstraction (deferred from Phase 6), plus
|
||||
presigned-URL uploads for large files.
|
||||
- **Phase 11 — Generated REST per collection** — PostgREST-style
|
||||
auto-CRUD + filtering over Collections documents; a clean read layer
|
||||
on data that already exists.
|
||||
- **Notifications** — alert rules on telemetry (error-rate thresholds,
|
||||
heartbeat silence) fanning out through the webhook dispatcher.
|
||||
- **Inbound webhooks** — project-scoped ingest endpoints that turn
|
||||
external events into domain events/jobs.
|
||||
- **HA mode** — leader-elected scheduler + distributed locks so two
|
||||
backend replicas don't double-fire jobs; Postgres advisory locks are
|
||||
enough at this scale.
|
||||
- **Email surface** — transactional templates + send log in the
|
||||
dashboard, backed by the auth service's existing SMTP/Resend wiring.
|
||||
|
||||
---
|
||||
|
||||
## What is deliberately out of scope
|
||||
|
||||
- Managed/hosted tier — the product is the self-hosted thing.
|
||||
- Realtime subscriptions, edge functions, generated REST/GraphQL per-table
|
||||
(Supabase PostgREST-style). Revisit after Phase 3 if wanted — Collections
|
||||
already cover the common case.
|
||||
- Per-request compute billing / hosted function runtime — if the
|
||||
Functions phase lands it stays user-managed infrastructure.
|
||||
- Embedding DBX's desktop UI — we use its MCP server as a library, not its app.
|
||||
Reference in new issue
Block a user