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:
Tomas DvorakandDevin committed 2026-09-18 15:15:01 +02:00
1 parent 4b7fdf35ad
commit 9e9a3d5f98
49 files changed
+3555 -32

No files matched your search

@@ -38,6 +38,7 @@ export { CreateIntegrationRequest } from './models/CreateIntegrationRequest';
export { CreateInvitationRequest } from './models/CreateInvitationRequest';
export type { CreateOrganizationRequest } from './models/CreateOrganizationRequest';
export type { CreateProjectRequest } from './models/CreateProjectRequest';
export type { CreateScheduledJobRequest } from './models/CreateScheduledJobRequest';
export type { CreateWebhookRequest } from './models/CreateWebhookRequest';
export type { DatabaseQuery } from './models/DatabaseQuery';
export type { DBConnection } from './models/DBConnection';
@@ -75,6 +76,7 @@ export type { IntegrationListResponse } from './models/IntegrationListResponse';
export type { IntegrationTestResult } from './models/IntegrationTestResult';
export type { Invitation } from './models/Invitation';
export type { InvitationID } from './models/InvitationID';
export type { JobID } from './models/JobID';
export type { MeResponse } from './models/MeResponse';
export type { MetricPoint } from './models/MetricPoint';
export type { MetricSeriesResponse } from './models/MetricSeriesResponse';
@@ -95,6 +97,11 @@ export type { ProjectSummary } from './models/ProjectSummary';
export type { ReadinessStatus } from './models/ReadinessStatus';
export type { RybbitMetricItem } from './models/RybbitMetricItem';
export type { RybbitSite } from './models/RybbitSite';
export { ScheduledJob } from './models/ScheduledJob';
export type { ScheduledJobCreateResponse } from './models/ScheduledJobCreateResponse';
export type { ScheduledJobListResponse } from './models/ScheduledJobListResponse';
export { ScheduledJobRun } from './models/ScheduledJobRun';
export type { ScheduledJobRunListResponse } from './models/ScheduledJobRunListResponse';
export type { SchemaQuery } from './models/SchemaQuery';
export type { TableQuery } from './models/TableQuery';
export type { TelemetryComponent } from './models/TelemetryComponent';
@@ -112,6 +119,7 @@ export { UpdateOrganizationMemberRoleRequest } from './models/UpdateOrganization
export type { UpdateOrganizationRequest } from './models/UpdateOrganizationRequest';
export { UpdateProjectMemberRoleRequest } from './models/UpdateProjectMemberRoleRequest';
export type { UpdateProjectRequest } from './models/UpdateProjectRequest';
export type { UpdateScheduledJobRequest } from './models/UpdateScheduledJobRequest';
export type { UpdateWebhookRequest } from './models/UpdateWebhookRequest';
export type { UserID } from './models/UserID';
export type { UserSummary } from './models/UserSummary';
@@ -153,6 +161,7 @@ export { $CreateIntegrationRequest } from './schemas/$CreateIntegrationRequest';
export { $CreateInvitationRequest } from './schemas/$CreateInvitationRequest';
export { $CreateOrganizationRequest } from './schemas/$CreateOrganizationRequest';
export { $CreateProjectRequest } from './schemas/$CreateProjectRequest';
export { $CreateScheduledJobRequest } from './schemas/$CreateScheduledJobRequest';
export { $CreateWebhookRequest } from './schemas/$CreateWebhookRequest';
export { $DatabaseQuery } from './schemas/$DatabaseQuery';
export { $DBConnection } from './schemas/$DBConnection';
@@ -190,6 +199,7 @@ export { $IntegrationListResponse } from './schemas/$IntegrationListResponse';
export { $IntegrationTestResult } from './schemas/$IntegrationTestResult';
export { $Invitation } from './schemas/$Invitation';
export { $InvitationID } from './schemas/$InvitationID';
export { $JobID } from './schemas/$JobID';
export { $MeResponse } from './schemas/$MeResponse';
export { $MetricPoint } from './schemas/$MetricPoint';
export { $MetricSeriesResponse } from './schemas/$MetricSeriesResponse';
@@ -210,6 +220,11 @@ export { $ProjectSummary } from './schemas/$ProjectSummary';
export { $ReadinessStatus } from './schemas/$ReadinessStatus';
export { $RybbitMetricItem } from './schemas/$RybbitMetricItem';
export { $RybbitSite } from './schemas/$RybbitSite';
export { $ScheduledJob } from './schemas/$ScheduledJob';
export { $ScheduledJobCreateResponse } from './schemas/$ScheduledJobCreateResponse';
export { $ScheduledJobListResponse } from './schemas/$ScheduledJobListResponse';
export { $ScheduledJobRun } from './schemas/$ScheduledJobRun';
export { $ScheduledJobRunListResponse } from './schemas/$ScheduledJobRunListResponse';
export { $SchemaQuery } from './schemas/$SchemaQuery';
export { $TableQuery } from './schemas/$TableQuery';
export { $TelemetryComponent } from './schemas/$TelemetryComponent';
@@ -227,6 +242,7 @@ export { $UpdateOrganizationMemberRoleRequest } from './schemas/$UpdateOrganizat
export { $UpdateOrganizationRequest } from './schemas/$UpdateOrganizationRequest';
export { $UpdateProjectMemberRoleRequest } from './schemas/$UpdateProjectMemberRoleRequest';
export { $UpdateProjectRequest } from './schemas/$UpdateProjectRequest';
export { $UpdateScheduledJobRequest } from './schemas/$UpdateScheduledJobRequest';
export { $UpdateWebhookRequest } from './schemas/$UpdateWebhookRequest';
export { $UserID } from './schemas/$UserID';
export { $UserSummary } from './schemas/$UserSummary';
@@ -237,6 +253,7 @@ export { $WebhookDeliveryListResponse } from './schemas/$WebhookDeliveryListResp
export { $WebhookID } from './schemas/$WebhookID';
export { $WebhookListResponse } from './schemas/$WebhookListResponse';
export { AutomationService } from './services/AutomationService';
export { CollectionsService } from './services/CollectionsService';
export { DatabasesService } from './services/DatabasesService';
export { HealthService } from './services/HealthService';