autopulse_database/
models.rs

1use autopulse_utils::{generate_uuid, Rewrite};
2use chrono::NaiveDateTime;
3use diesel::prelude::*;
4use serde::Serialize;
5use std::fmt::Display;
6
7/// The status of a scan event being proccessed by [Targets](crate::service::targets).
8#[derive(Serialize)]
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 String {
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        .to_string()
53    }
54}
55
56/// Represents a scan event.
57///
58/// A scan event is created when a file is added by [Triggers](crate::service::triggers).
59#[derive(
60    Queryable, Selectable, Serialize, Clone, Debug, AsChangeset, Identifiable, Hash, Eq, PartialEq,
61)]
62#[diesel(table_name = crate::schema::scan_events)]
63pub struct ScanEvent {
64    /// The [uuid](crate::utils::generate_uuid::generate_uuid) of the scan event.
65    pub id: String,
66
67    /// The name of the Trigger that created the scan event.
68    pub event_source: String,
69    /// The time the scan event was created.
70    pub event_timestamp: NaiveDateTime,
71
72    /// The rewritten path of the file.
73    pub file_path: String,
74    /// Optional hash of the file.
75    pub file_hash: Option<String>,
76    /// The status of the scan event being processed.
77    pub process_status: String,
78    /// The status of the file being found.
79    pub found_status: String,
80
81    /// 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).
82    pub failed_times: i32,
83    /// The time the scan event will be retried.
84    pub next_retry_at: Option<chrono::NaiveDateTime>,
85
86    /// The targets that have been hit by the scan event delimited by a comma.
87    pub targets_hit: String,
88
89    /// The time the file was found.
90    pub found_at: Option<chrono::NaiveDateTime>,
91    /// The time the scan event was processed.
92    pub processed_at: Option<chrono::NaiveDateTime>,
93
94    /// The time the scan event was created.
95    pub created_at: NaiveDateTime,
96    /// The time the scan event was updated.
97    pub updated_at: NaiveDateTime,
98
99    /// The time the scan event can be processed.
100    pub can_process: NaiveDateTime,
101}
102
103impl ScanEvent {
104    pub fn get_targets_hit(&self) -> Vec<String> {
105        self.targets_hit
106            .split(',')
107            .map(|s| s.to_string())
108            .filter(|s| !s.is_empty())
109            .collect()
110    }
111
112    pub fn add_target_hit(&mut self, target: &str) {
113        let mut targets = self.get_targets_hit();
114        targets.push(target.to_string());
115        targets.sort();
116        targets.dedup();
117        self.targets_hit = targets.join(",");
118    }
119
120    pub fn get_path(&self, rewrite: &Option<Rewrite>) -> String {
121        rewrite.as_ref().map_or_else(
122            || self.file_path.clone(),
123            |rewrite| rewrite.rewrite_path(self.file_path.clone()),
124        )
125    }
126}
127
128#[derive(Insertable)]
129#[diesel(table_name = crate::schema::scan_events)]
130#[doc(hidden)]
131pub struct NewScanEvent {
132    pub id: String,
133    pub event_source: String,
134
135    pub file_path: String,
136    pub file_hash: Option<String>,
137
138    pub found_status: String,
139    pub can_process: NaiveDateTime,
140}
141
142impl Default for NewScanEvent {
143    fn default() -> Self {
144        Self {
145            id: generate_uuid(),
146            event_source: "unknown".to_string(),
147            file_path: "unknown".to_string(),
148            file_hash: None,
149            found_status: FoundStatus::NotFound.into(),
150            can_process: chrono::Utc::now().naive_utc(),
151        }
152    }
153}