Skip to main content

autopulse_database/
models.rs

1use autopulse_utils::{generate_uuid, Rewrite};
2use chrono::NaiveDateTime;
3use diesel::prelude::*;
4use serde::Serialize;
5use std::{fmt::Display, str::FromStr};
6
7/// The status of a scan event being proccessed by [Targets](crate::service::targets).
8#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq)]
9pub enum ProcessStatus {
10    Pending,
11    Complete,
12    Retry,
13    Failed,
14}
15
16/// Whether a file was found or not.
17///
18/// Note: only used if [`opts.check_path`](autopulse_service::settings::opts::Opts::check_path) is set.
19#[derive(Serialize)]
20pub enum FoundStatus {
21    Found,
22    NotFound,
23    HashMismatch,
24}
25
26impl Display for FoundStatus {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        let status = match self {
29            Self::Found => "found",
30            Self::NotFound => "not_found",
31            Self::HashMismatch => "hash_mismatch",
32        };
33
34        write!(f, "{status}")
35    }
36}
37
38impl From<FoundStatus> for String {
39    fn from(val: FoundStatus) -> Self {
40        val.to_string()
41    }
42}
43
44impl From<ProcessStatus> for &'static str {
45    fn from(val: ProcessStatus) -> Self {
46        match val {
47            ProcessStatus::Pending => "pending",
48            ProcessStatus::Complete => "complete",
49            ProcessStatus::Retry => "retry",
50            ProcessStatus::Failed => "failed",
51        }
52    }
53}
54
55impl From<ProcessStatus> for String {
56    fn from(val: ProcessStatus) -> Self {
57        <&'static str>::from(val).to_string()
58    }
59}
60
61impl FromStr for ProcessStatus {
62    type Err = ();
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        match s {
65            "pending" => Ok(Self::Pending),
66            "complete" => Ok(Self::Complete),
67            "retry" => Ok(Self::Retry),
68            "failed" => Ok(Self::Failed),
69            _ => Err(()),
70        }
71    }
72}
73
74/// Represents a scan event.
75///
76/// A scan event is created when a file is added by [Triggers](crate::service::triggers).
77#[derive(
78    Queryable, Selectable, Serialize, Clone, Debug, AsChangeset, Identifiable, Hash, Eq, PartialEq,
79)]
80#[diesel(table_name = crate::schema::scan_events)]
81pub struct ScanEvent {
82    /// The [uuid](crate::utils::generate_uuid::generate_uuid) of the scan event.
83    pub id: String,
84
85    /// The name of the Trigger that created the scan event.
86    pub event_source: String,
87    /// The time the scan event was created.
88    pub event_timestamp: NaiveDateTime,
89
90    /// The rewritten path of the file.
91    pub file_path: String,
92    /// Optional hash of the file.
93    pub file_hash: Option<String>,
94    /// The status of the scan event being processed.
95    pub process_status: String,
96    /// The status of the file being found.
97    pub found_status: String,
98
99    /// The number of times the scan event has failed. Used for retries and is limited to [`opts.max_retries`](/autopulse_service/settings/opts/struct.Opts.html#structfield.max_retries).
100    pub failed_times: i32,
101    /// The time the scan event will be retried.
102    pub next_retry_at: Option<chrono::NaiveDateTime>,
103
104    /// The targets that have been hit by the scan event delimited by a comma.
105    pub targets_hit: String,
106
107    /// The time the file was found.
108    pub found_at: Option<chrono::NaiveDateTime>,
109    /// The time the scan event was processed.
110    pub processed_at: Option<chrono::NaiveDateTime>,
111
112    /// The time the scan event was created.
113    pub created_at: NaiveDateTime,
114    /// The time the scan event was updated.
115    pub updated_at: NaiveDateTime,
116
117    /// The time the scan event can be processed.
118    pub can_process: NaiveDateTime,
119}
120
121impl ScanEvent {
122    pub fn get_targets_hit(&self) -> Vec<String> {
123        self.targets_hit
124            .split(',')
125            .map(|s| s.to_string())
126            .filter(|s| !s.is_empty())
127            .collect()
128    }
129
130    pub fn add_target_hit(&mut self, target: &str) {
131        let mut targets = self.get_targets_hit();
132        targets.push(target.to_string());
133        targets.sort();
134        targets.dedup();
135        self.targets_hit = targets.join(",");
136    }
137
138    pub fn get_path(&self, rewrite: &Option<Rewrite>) -> String {
139        rewrite.as_ref().map_or_else(
140            || self.file_path.clone(),
141            |rewrite| rewrite.rewrite_path(self.file_path.clone()),
142        )
143    }
144}
145
146#[derive(Insertable)]
147#[diesel(table_name = crate::schema::scan_events)]
148#[doc(hidden)]
149pub struct NewScanEvent {
150    pub id: String,
151    pub event_source: String,
152
153    pub file_path: String,
154    pub file_hash: Option<String>,
155
156    pub found_status: String,
157    pub can_process: NaiveDateTime,
158}
159
160impl Default for NewScanEvent {
161    fn default() -> Self {
162        Self {
163            id: generate_uuid(),
164            event_source: "unknown".to_string(),
165            file_path: "unknown".to_string(),
166            file_hash: None,
167            found_status: FoundStatus::NotFound.into(),
168            can_process: chrono::Utc::now().naive_utc(),
169        }
170    }
171}
172
173/// Key/value store; currently holds only the UI session-signing key.
174#[derive(Queryable, Selectable, Insertable, Clone, Debug)]
175#[diesel(table_name = crate::schema::app_state)]
176pub struct AppState {
177    pub key: String,
178    pub value: Vec<u8>,
179    pub updated_at: NaiveDateTime,
180}