package store import ( "context" "time" ) type Run struct { ID int64 `json:"id"` TaskID int64 `json:"task_id"` Status string `json:"status"` StartedAt time.Time `json:"started_at"` FinishedAt *time.Time `json:"finished_at"` TotalCopied int64 `json:"total_copied"` TotalSkipped int64 `json:"total_skipped"` TotalErrors int64 `json:"total_errors"` Trigger string `json:"trigger"` } func (s *Store) CreateRun(ctx context.Context, taskID int64, trigger string) (int64, error) { var id int64 err := s.Pool.QueryRow(ctx, `INSERT INTO runs (task_id, trigger) VALUES ($1,$2) RETURNING id`, taskID, trigger).Scan(&id) return id, err } func (s *Store) FinishRun(ctx context.Context, id int64, status string, copied, skipped, errs int64) error { _, err := s.Pool.Exec(ctx, `UPDATE runs SET status=$2, finished_at=now(), total_copied=$3, total_skipped=$4, total_errors=$5 WHERE id=$1`, id, status, copied, skipped, errs) return err } // ListRunsByTask returns a task's runs, newest first, for the run-log modal. func (s *Store) ListRunsByTask(ctx context.Context, taskID int64) ([]Run, error) { rows, err := s.Pool.Query(ctx, `SELECT id, task_id, started_at, finished_at, status, total_copied, total_skipped, total_errors, trigger FROM runs WHERE task_id=$1 ORDER BY id DESC`, taskID) if err != nil { return nil, err } defer rows.Close() out := []Run{} for rows.Next() { var r Run if err := rows.Scan(&r.ID, &r.TaskID, &r.StartedAt, &r.FinishedAt, &r.Status, &r.TotalCopied, &r.TotalSkipped, &r.TotalErrors, &r.Trigger); err != nil { return nil, err } out = append(out, r) } return out, rows.Err() } // LastFinishedRunAt returns the most recent finished run's timestamp, or nil if // the task has never completed a run — the baseline for the next scheduled run. func (s *Store) LastFinishedRunAt(ctx context.Context, taskID int64) (*time.Time, error) { var t *time.Time err := s.Pool.QueryRow(ctx, `SELECT max(finished_at) FROM runs WHERE task_id=$1 AND finished_at IS NOT NULL`, taskID).Scan(&t) return t, err }