use super::types::{McScheduleItem, McScheduleKind};
use crate::db::Pool;
use crate::db::models::{CronJob, CronJobRun};
use crate::db::repository::{CronJobRepository, CronJobRunRepository};
pub async fn list(pool: Pool) -> Vec<McScheduleItem> {
let repo = CronJobRepository::new(pool.clone());
let run_repo = CronJobRunRepository::new(pool);
let jobs = match repo.list_all().await {
Ok(j) => j,
Err(e) => {
tracing::warn!("schedule_service: failed to list cron jobs: {e:#}");
return Vec::new();
}
};
let recent_runs = run_repo.list_recent(100).await.unwrap_or_default();
let mut last_runs: std::collections::HashMap<String, &CronJobRun> =
std::collections::HashMap::new();
for run in &recent_runs {
let job_id = run.job_id.to_string();
last_runs.entry(job_id).or_insert(run);
}
jobs.into_iter()
.map(|job| item_from_cron(job, &last_runs))
.collect()
}
fn item_from_cron(
job: CronJob,
last_runs: &std::collections::HashMap<String, &CronJobRun>,
) -> McScheduleItem {
let schedule = format_cron_schedule(&job);
let job_id_str = job.id.to_string();
let last_run = last_runs.get(&job_id_str);
let last_run_status = last_run.map(|r| r.status.clone());
let last_run_cost = last_run.map(|r| r.cost);
let last_run_duration_secs =
last_run.and_then(|r| r.completed_at.map(|c| (c - r.started_at).num_seconds()));
McScheduleItem {
id: job_id_str,
label: job.name,
schedule,
kind: McScheduleKind::Cron,
awaiting_user: !job.enabled,
prompt: job.prompt,
deliver_to: job.deliver_to,
last_run_at: job.last_run_at,
next_run_at: job.next_run_at,
created_at: job.created_at,
enabled: job.enabled,
profile_name: job.profile_name,
last_run_status,
last_run_cost,
last_run_duration_secs,
}
}
fn format_cron_schedule(job: &CronJob) -> String {
let mut parts: Vec<String> = vec![job.cron_expr.clone()];
if !job.timezone.is_empty() && job.timezone != "UTC" {
parts.push(format!("({})", job.timezone));
}
if !job.enabled {
parts.push("paused".to_string());
} else if let Some(next) = job.next_run_at {
parts.push(format!("next {}", next.format("%Y-%m-%d %H:%M")));
}
parts.join(" ")
}