Skip to main content

systemprompt_models/services/
scheduler.rs

1use serde::{Deserialize, Serialize};
2use systemprompt_identifiers::UserId;
3
4use super::SystemAdmin;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(deny_unknown_fields)]
8pub struct JobConfig {
9    #[serde(default)]
10    pub extension: Option<String>,
11    pub name: String,
12    pub owner: UserId,
13    #[serde(default = "default_true")]
14    pub enabled: bool,
15    #[serde(default)]
16    pub schedule: Option<String>,
17}
18
19const fn default_true() -> bool {
20    true
21}
22
23impl JobConfig {
24    #[must_use]
25    pub fn new(name: impl Into<String>, owner: UserId) -> Self {
26        Self {
27            extension: None,
28            name: name.into(),
29            owner,
30            enabled: true,
31            schedule: None,
32        }
33    }
34
35    #[must_use]
36    pub fn with_extension(mut self, extension: impl Into<String>) -> Self {
37        self.extension = Some(extension.into());
38        self
39    }
40
41    #[must_use]
42    pub fn with_schedule(mut self, schedule: impl Into<String>) -> Self {
43        self.schedule = Some(schedule.into());
44        self
45    }
46
47    #[must_use]
48    pub const fn disabled(mut self) -> Self {
49        self.enabled = false;
50        self
51    }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct SchedulerConfig {
56    #[serde(default = "default_true")]
57    pub enabled: bool,
58    #[serde(default)]
59    pub jobs: Vec<JobConfig>,
60    #[serde(default = "default_bootstrap_jobs")]
61    pub bootstrap_jobs: Vec<String>,
62    #[serde(default = "default_true")]
63    pub distributed_lock: bool,
64}
65
66fn default_bootstrap_jobs() -> Vec<String> {
67    vec![
68        "database_cleanup".to_owned(),
69        "cleanup_inactive_sessions".to_owned(),
70    ]
71}
72
73impl SchedulerConfig {
74    // The four cleanup jobs (cleanup_anonymous_users, cleanup_empty_contexts,
75    // cleanup_inactive_sessions, database_cleanup) have no human originator;
76    // attributing them to the system admin row keeps the scheduler's
77    // resolve_owners pass green against a real user.
78    #[must_use]
79    pub fn with_system_admin(admin: &SystemAdmin) -> Self {
80        let owner = admin.id().clone();
81        Self {
82            enabled: true,
83            jobs: vec![
84                JobConfig::new("cleanup_anonymous_users", owner.clone())
85                    .with_extension("core")
86                    .with_schedule("0 0 3 * * *"),
87                JobConfig::new("cleanup_empty_contexts", owner.clone())
88                    .with_extension("core")
89                    .with_schedule("0 0 * * * *"),
90                JobConfig::new("cleanup_inactive_sessions", owner.clone())
91                    .with_extension("core")
92                    .with_schedule("0 0 * * * *"),
93                JobConfig::new("database_cleanup", owner)
94                    .with_extension("core")
95                    .with_schedule("0 0 4 * * *"),
96            ],
97            bootstrap_jobs: default_bootstrap_jobs(),
98            distributed_lock: true,
99        }
100    }
101}