Skip to main content

systemprompt_models/services/
scheduler.rs

1use serde::{Deserialize, Serialize};
2use systemprompt_identifiers::UserId;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5#[serde(deny_unknown_fields)]
6pub struct JobConfig {
7    #[serde(default)]
8    pub extension: Option<String>,
9    pub name: String,
10    #[serde(default)]
11    pub owner: Option<UserId>,
12    #[serde(default = "default_true")]
13    pub enabled: bool,
14    #[serde(default)]
15    pub schedule: Option<String>,
16}
17
18const fn default_true() -> bool {
19    true
20}
21
22impl JobConfig {
23    /// A job with no explicit `owner` runs as the profile `system_admin`,
24    /// resolved per-environment at scheduler start. Set one with
25    /// [`Self::with_owner`] only for a job that must run as a specific user.
26    #[must_use]
27    pub fn new(name: impl Into<String>) -> Self {
28        Self {
29            extension: None,
30            name: name.into(),
31            owner: None,
32            enabled: true,
33            schedule: None,
34        }
35    }
36
37    #[must_use]
38    pub fn with_owner(mut self, owner: UserId) -> Self {
39        self.owner = Some(owner);
40        self
41    }
42
43    #[must_use]
44    pub fn with_extension(mut self, extension: impl Into<String>) -> Self {
45        self.extension = Some(extension.into());
46        self
47    }
48
49    #[must_use]
50    pub fn with_schedule(mut self, schedule: impl Into<String>) -> Self {
51        self.schedule = Some(schedule.into());
52        self
53    }
54
55    #[must_use]
56    pub const fn disabled(mut self) -> Self {
57        self.enabled = false;
58        self
59    }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SchedulerConfig {
64    #[serde(default = "default_true")]
65    pub enabled: bool,
66    #[serde(default)]
67    pub jobs: Vec<JobConfig>,
68    #[serde(default = "default_bootstrap_jobs")]
69    pub bootstrap_jobs: Vec<String>,
70    #[serde(default = "default_true")]
71    pub distributed_lock: bool,
72}
73
74fn default_bootstrap_jobs() -> Vec<String> {
75    vec![
76        "database_cleanup".to_owned(),
77        "cleanup_inactive_sessions".to_owned(),
78    ]
79}
80
81impl SchedulerConfig {
82    /// The built-in core job set. The four cleanup jobs
83    /// (`cleanup_anonymous_users`, `cleanup_empty_contexts`,
84    /// `cleanup_inactive_sessions`, `database_cleanup`) have no human
85    /// originator, so they carry no explicit `owner` and run as the profile
86    /// `system_admin` resolved per-environment at scheduler start.
87    #[must_use]
88    pub fn with_system_admin() -> Self {
89        Self {
90            enabled: true,
91            jobs: vec![
92                JobConfig::new("cleanup_anonymous_users")
93                    .with_extension("core")
94                    .with_schedule("0 0 3 * * *"),
95                JobConfig::new("cleanup_empty_contexts")
96                    .with_extension("core")
97                    .with_schedule("0 0 * * * *"),
98                JobConfig::new("cleanup_inactive_sessions")
99                    .with_extension("core")
100                    .with_schedule("0 0 * * * *"),
101                JobConfig::new("database_cleanup")
102                    .with_extension("core")
103                    .with_schedule("0 0 4 * * *"),
104            ],
105            bootstrap_jobs: default_bootstrap_jobs(),
106            distributed_lock: true,
107        }
108    }
109}