mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-28 07:41: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>
442 lines
11 KiB
Go
442 lines
11 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.29.0
|
|
// source: jobs.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const createScheduledJob = `-- name: CreateScheduledJob :one
|
|
INSERT INTO core.scheduled_jobs (
|
|
project_id,
|
|
name,
|
|
schedule,
|
|
url,
|
|
secret,
|
|
payload,
|
|
enabled,
|
|
next_run_at,
|
|
created_by_user_id
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
RETURNING id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at
|
|
`
|
|
|
|
type CreateScheduledJobParams struct {
|
|
ProjectID uuid.UUID `json:"project_id"`
|
|
Name string `json:"name"`
|
|
Schedule string `json:"schedule"`
|
|
Url string `json:"url"`
|
|
Secret []byte `json:"secret"`
|
|
Payload []byte `json:"payload"`
|
|
Enabled bool `json:"enabled"`
|
|
NextRunAt pgtype.Timestamptz `json:"next_run_at"`
|
|
CreatedByUserID pgtype.UUID `json:"created_by_user_id"`
|
|
}
|
|
|
|
func (q *Queries) CreateScheduledJob(ctx context.Context, arg CreateScheduledJobParams) (CoreScheduledJob, error) {
|
|
row := q.db.QueryRow(ctx, createScheduledJob,
|
|
arg.ProjectID,
|
|
arg.Name,
|
|
arg.Schedule,
|
|
arg.Url,
|
|
arg.Secret,
|
|
arg.Payload,
|
|
arg.Enabled,
|
|
arg.NextRunAt,
|
|
arg.CreatedByUserID,
|
|
)
|
|
var i CoreScheduledJob
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deleteScheduledJob = `-- name: DeleteScheduledJob :one
|
|
DELETE FROM core.scheduled_jobs
|
|
WHERE id = $1
|
|
AND project_id = $2
|
|
RETURNING id
|
|
`
|
|
|
|
type DeleteScheduledJobParams struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ProjectID uuid.UUID `json:"project_id"`
|
|
}
|
|
|
|
func (q *Queries) DeleteScheduledJob(ctx context.Context, arg DeleteScheduledJobParams) (uuid.UUID, error) {
|
|
row := q.db.QueryRow(ctx, deleteScheduledJob, arg.ID, arg.ProjectID)
|
|
var id uuid.UUID
|
|
err := row.Scan(&id)
|
|
return id, err
|
|
}
|
|
|
|
const finishScheduledJobRun = `-- name: FinishScheduledJobRun :one
|
|
UPDATE core.scheduled_job_runs
|
|
SET status = $2,
|
|
status_code = $3,
|
|
error = $4,
|
|
duration_ms = $5,
|
|
finished_at = NOW()
|
|
WHERE id = $1
|
|
RETURNING id, job_id, status, triggered_by, status_code, error, duration_ms, created_at, finished_at
|
|
`
|
|
|
|
type FinishScheduledJobRunParams struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Status string `json:"status"`
|
|
StatusCode *int32 `json:"status_code"`
|
|
Error string `json:"error"`
|
|
DurationMs *int32 `json:"duration_ms"`
|
|
}
|
|
|
|
func (q *Queries) FinishScheduledJobRun(ctx context.Context, arg FinishScheduledJobRunParams) (CoreScheduledJobRun, error) {
|
|
row := q.db.QueryRow(ctx, finishScheduledJobRun,
|
|
arg.ID,
|
|
arg.Status,
|
|
arg.StatusCode,
|
|
arg.Error,
|
|
arg.DurationMs,
|
|
)
|
|
var i CoreScheduledJobRun
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.JobID,
|
|
&i.Status,
|
|
&i.TriggeredBy,
|
|
&i.StatusCode,
|
|
&i.Error,
|
|
&i.DurationMs,
|
|
&i.CreatedAt,
|
|
&i.FinishedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getScheduledJobByID = `-- name: GetScheduledJobByID :one
|
|
SELECT id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at FROM core.scheduled_jobs
|
|
WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetScheduledJobByID(ctx context.Context, id uuid.UUID) (CoreScheduledJob, error) {
|
|
row := q.db.QueryRow(ctx, getScheduledJobByID, id)
|
|
var i CoreScheduledJob
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const insertScheduledJobRun = `-- name: InsertScheduledJobRun :one
|
|
INSERT INTO core.scheduled_job_runs (job_id, triggered_by)
|
|
VALUES ($1, $2)
|
|
RETURNING id, job_id, status, triggered_by, status_code, error, duration_ms, created_at, finished_at
|
|
`
|
|
|
|
type InsertScheduledJobRunParams struct {
|
|
JobID uuid.UUID `json:"job_id"`
|
|
TriggeredBy string `json:"triggered_by"`
|
|
}
|
|
|
|
func (q *Queries) InsertScheduledJobRun(ctx context.Context, arg InsertScheduledJobRunParams) (CoreScheduledJobRun, error) {
|
|
row := q.db.QueryRow(ctx, insertScheduledJobRun, arg.JobID, arg.TriggeredBy)
|
|
var i CoreScheduledJobRun
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.JobID,
|
|
&i.Status,
|
|
&i.TriggeredBy,
|
|
&i.StatusCode,
|
|
&i.Error,
|
|
&i.DurationMs,
|
|
&i.CreatedAt,
|
|
&i.FinishedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listDueScheduledJobs = `-- name: ListDueScheduledJobs :many
|
|
SELECT id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at FROM core.scheduled_jobs
|
|
WHERE enabled
|
|
AND next_run_at IS NOT NULL
|
|
AND next_run_at <= $1
|
|
`
|
|
|
|
func (q *Queries) ListDueScheduledJobs(ctx context.Context, nextRunAt pgtype.Timestamptz) ([]CoreScheduledJob, error) {
|
|
rows, err := q.db.Query(ctx, listDueScheduledJobs, nextRunAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []CoreScheduledJob
|
|
for rows.Next() {
|
|
var i CoreScheduledJob
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listScheduledJobRuns = `-- name: ListScheduledJobRuns :many
|
|
SELECT id, job_id, status, triggered_by, status_code, error, duration_ms, created_at, finished_at FROM core.scheduled_job_runs
|
|
WHERE job_id = $1
|
|
ORDER BY created_at DESC
|
|
LIMIT $2
|
|
`
|
|
|
|
type ListScheduledJobRunsParams struct {
|
|
JobID uuid.UUID `json:"job_id"`
|
|
Limit int32 `json:"limit"`
|
|
}
|
|
|
|
func (q *Queries) ListScheduledJobRuns(ctx context.Context, arg ListScheduledJobRunsParams) ([]CoreScheduledJobRun, error) {
|
|
rows, err := q.db.Query(ctx, listScheduledJobRuns, arg.JobID, arg.Limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []CoreScheduledJobRun
|
|
for rows.Next() {
|
|
var i CoreScheduledJobRun
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.JobID,
|
|
&i.Status,
|
|
&i.TriggeredBy,
|
|
&i.StatusCode,
|
|
&i.Error,
|
|
&i.DurationMs,
|
|
&i.CreatedAt,
|
|
&i.FinishedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listScheduledJobs = `-- name: ListScheduledJobs :many
|
|
SELECT id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at FROM core.scheduled_jobs
|
|
WHERE project_id = $1
|
|
ORDER BY created_at ASC
|
|
`
|
|
|
|
func (q *Queries) ListScheduledJobs(ctx context.Context, projectID uuid.UUID) ([]CoreScheduledJob, error) {
|
|
rows, err := q.db.Query(ctx, listScheduledJobs, projectID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []CoreScheduledJob
|
|
for rows.Next() {
|
|
var i CoreScheduledJob
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const markScheduledJobRan = `-- name: MarkScheduledJobRan :one
|
|
UPDATE core.scheduled_jobs
|
|
SET last_run_at = $2,
|
|
last_status = $3,
|
|
next_run_at = $4,
|
|
updated_at = NOW()
|
|
WHERE id = $1
|
|
RETURNING id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at
|
|
`
|
|
|
|
type MarkScheduledJobRanParams struct {
|
|
ID uuid.UUID `json:"id"`
|
|
LastRunAt pgtype.Timestamptz `json:"last_run_at"`
|
|
LastStatus string `json:"last_status"`
|
|
NextRunAt pgtype.Timestamptz `json:"next_run_at"`
|
|
}
|
|
|
|
func (q *Queries) MarkScheduledJobRan(ctx context.Context, arg MarkScheduledJobRanParams) (CoreScheduledJob, error) {
|
|
row := q.db.QueryRow(ctx, markScheduledJobRan,
|
|
arg.ID,
|
|
arg.LastRunAt,
|
|
arg.LastStatus,
|
|
arg.NextRunAt,
|
|
)
|
|
var i CoreScheduledJob
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const pruneScheduledJobRuns = `-- name: PruneScheduledJobRuns :exec
|
|
DELETE FROM core.scheduled_job_runs r
|
|
WHERE r.job_id = $1
|
|
AND r.id NOT IN (
|
|
SELECT k.id FROM core.scheduled_job_runs k
|
|
WHERE k.job_id = $1
|
|
ORDER BY k.created_at DESC
|
|
LIMIT $2
|
|
)
|
|
`
|
|
|
|
type PruneScheduledJobRunsParams struct {
|
|
JobID uuid.UUID `json:"job_id"`
|
|
Limit int32 `json:"limit"`
|
|
}
|
|
|
|
func (q *Queries) PruneScheduledJobRuns(ctx context.Context, arg PruneScheduledJobRunsParams) error {
|
|
_, err := q.db.Exec(ctx, pruneScheduledJobRuns, arg.JobID, arg.Limit)
|
|
return err
|
|
}
|
|
|
|
const updateScheduledJob = `-- name: UpdateScheduledJob :one
|
|
UPDATE core.scheduled_jobs
|
|
SET name = $3,
|
|
schedule = $4,
|
|
url = $5,
|
|
enabled = $6,
|
|
next_run_at = $7,
|
|
secret = CASE WHEN $8::boolean THEN $9 ELSE secret END,
|
|
payload = CASE WHEN $10::boolean THEN $11 ELSE payload END,
|
|
updated_at = NOW()
|
|
WHERE id = $1
|
|
AND project_id = $2
|
|
RETURNING id, project_id, name, schedule, url, secret, payload, enabled, last_run_at, last_status, next_run_at, created_by_user_id, created_at, updated_at
|
|
`
|
|
|
|
type UpdateScheduledJobParams struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ProjectID uuid.UUID `json:"project_id"`
|
|
Name string `json:"name"`
|
|
Schedule string `json:"schedule"`
|
|
Url string `json:"url"`
|
|
Enabled bool `json:"enabled"`
|
|
NextRunAt pgtype.Timestamptz `json:"next_run_at"`
|
|
Column8 bool `json:"column_8"`
|
|
Secret []byte `json:"secret"`
|
|
Column10 bool `json:"column_10"`
|
|
Payload []byte `json:"payload"`
|
|
}
|
|
|
|
func (q *Queries) UpdateScheduledJob(ctx context.Context, arg UpdateScheduledJobParams) (CoreScheduledJob, error) {
|
|
row := q.db.QueryRow(ctx, updateScheduledJob,
|
|
arg.ID,
|
|
arg.ProjectID,
|
|
arg.Name,
|
|
arg.Schedule,
|
|
arg.Url,
|
|
arg.Enabled,
|
|
arg.NextRunAt,
|
|
arg.Column8,
|
|
arg.Secret,
|
|
arg.Column10,
|
|
arg.Payload,
|
|
)
|
|
var i CoreScheduledJob
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.ProjectID,
|
|
&i.Name,
|
|
&i.Schedule,
|
|
&i.Url,
|
|
&i.Secret,
|
|
&i.Payload,
|
|
&i.Enabled,
|
|
&i.LastRunAt,
|
|
&i.LastStatus,
|
|
&i.NextRunAt,
|
|
&i.CreatedByUserID,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|