mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 15:51:23 +00:00
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>
40 lines
1.5 KiB
SQL
40 lines
1.5 KiB
SQL
-- +goose Up
|
|
CREATE TABLE core.scheduled_jobs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
schedule TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
secret BYTEA NOT NULL DEFAULT ''::bytea,
|
|
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
last_run_at TIMESTAMPTZ,
|
|
last_status TEXT NOT NULL DEFAULT '' CHECK (last_status IN ('', 'success', 'failed')),
|
|
next_run_at TIMESTAMPTZ,
|
|
created_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (project_id, name)
|
|
);
|
|
|
|
CREATE INDEX idx_scheduled_jobs_due ON core.scheduled_jobs(next_run_at)
|
|
WHERE enabled AND next_run_at IS NOT NULL;
|
|
|
|
CREATE TABLE core.scheduled_job_runs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
job_id UUID NOT NULL REFERENCES core.scheduled_jobs(id) ON DELETE CASCADE,
|
|
status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'success', 'failed')),
|
|
triggered_by TEXT NOT NULL DEFAULT 'schedule' CHECK (triggered_by IN ('schedule', 'manual')),
|
|
status_code INT,
|
|
error TEXT NOT NULL DEFAULT '',
|
|
duration_ms INT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
finished_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE INDEX idx_scheduled_job_runs_job ON core.scheduled_job_runs(job_id, created_at DESC);
|
|
|
|
-- +goose Down
|
|
DROP TABLE IF EXISTS core.scheduled_job_runs;
|
|
DROP TABLE IF EXISTS core.scheduled_jobs;
|