Skip to main content

quorum_core/db/queries/
logs.rs

1//!  This file inclues query functions for writing different types of logs into the database.
2//!
3//! Currently this file incldues functions to write `server logs` and `audit logs`
4//!
5//! `server logs` are specifically for server only things. This can include start up, shutdown, server specific errors and
6//! other sever specific events. Typically server logs won't include anything user triggered.
7//!
8//! `audit logs` specificly for user triggered events. This is used for basically anything the user does which reaches the server.
9//! This can include API endpoints being called, logins, signups, and other triggered events.
10
11use crate::db::DB;
12use std::error::Error;
13
14pub struct ServerLogEntry {
15    pub timestamp: String,
16    pub event_type: String,
17    pub duration_ms: Option<f64>,
18    pub message: Option<String>,
19    pub error_code: Option<f64>,
20}
21
22pub struct AuditLogEntry {
23    pub created_at: String,
24    pub log_type: String,
25    pub action: Option<String>,
26    pub user_id: Option<String>,
27    pub target: Option<String>,
28}
29
30/// Retrieves server logs from the database.
31///
32/// # Arguments
33/// * `db` - A reference to the database connection.
34/// * `days` - An optional number of days to filter logs. If `None`, retrieves the last 100 logs.
35///
36/// # Returns
37/// * `Ok(Vec<ServerLogEntry>)` - A vector of server log entries if the query is successful.
38/// * `Err(Box<dyn Error + Send + Sync>)` - An error if the query fails.
39///
40/// # Examples
41/// ```rust
42/// let logs = get_server_logs(&db, Some(7)).await.unwrap(); // Get logs from the last 7 days
43/// let recent_logs = get_server_logs(&db, None).await.unwrap(); // Get the last 100 logs
44/// ```
45pub async fn get_server_logs(
46    db: &DB,
47    days: Option<u32>,
48) -> Result<Vec<ServerLogEntry>, Box<dyn Error + Send + Sync>> {
49    let query = match days {
50        Some(d) => format!(
51            "SELECT timestamp, event_type_id.name AS event_type, duration_ms, message, error_code
52             FROM server_logs
53             WHERE timestamp >= time::now() - {}d
54             ORDER BY timestamp DESC",
55            d
56        ),
57        None => {
58            "SELECT timestamp, event_type_id.name AS event_type, duration_ms, message, error_code
59                 FROM server_logs
60                 ORDER BY timestamp DESC
61                 LIMIT 100"
62                .to_string()
63        }
64    };
65
66    let mut response = db.query(&query).await?;
67    let records: Vec<serde_json::Value> = response.take(0)?;
68
69    let entries = records
70        .into_iter()
71        .map(|r| ServerLogEntry {
72            timestamp: r["timestamp"].as_str().unwrap_or("unknown").to_string(),
73            event_type: r["event_type"].as_str().unwrap_or("unknown").to_string(),
74            duration_ms: r["duration_ms"].as_f64(),
75            message: r["message"].as_str().map(|s| s.to_string()),
76            error_code: r["error_code"].as_f64(),
77        })
78        .collect();
79
80    Ok(entries)
81}
82
83/// Retrieves audit logs from the database.
84///
85/// # Arguments
86/// * `db` - A reference to the database connection.
87/// * `days` - An optional number of days to filter logs. If `None`, retrieves the last 100 logs.
88///
89/// # Returns
90/// * `Ok(Vec<AuditLogEntry>)` - A vector of audit log entries if the query is successful.
91/// * `Err(Box<dyn Error + Send + Sync>)` - An error if the query fails.
92///
93/// # Examples
94/// ```rust
95/// let logs = get_audit_logs(&db, Some(7)).await.unwrap(); // Get logs from the last 7 days
96/// let recent_logs = get_audit_logs(&db, None).await.unwrap(); // Get the last 100 logs
97/// ```
98pub async fn get_audit_logs(
99    db: &DB,
100    days: Option<u32>,
101) -> Result<Vec<AuditLogEntry>, Box<dyn Error + Send + Sync>> {
102    let query = match days {
103        Some(d) => format!(
104            "SELECT created_at, log_type_id.name AS log_type, action_type_id.name AS action, user_id, target_type_table_id
105             FROM audit_logs
106             WHERE created_at >= time::now() - {}d
107             ORDER BY created_at DESC",
108            d
109        ),
110        None => "SELECT created_at, log_type_id.name AS log_type, action_type_id.name AS action, user_id, target_type_table_id
111                 FROM audit_logs
112                 ORDER BY created_at DESC
113                 LIMIT 100".to_string(),
114    };
115
116    let mut response = db.query(&query).await?;
117    let records: Vec<serde_json::Value> = response.take(0)?;
118
119    let entries = records
120        .into_iter()
121        .map(|r| AuditLogEntry {
122            created_at: r["created_at"].as_str().unwrap_or("unknown").to_string(),
123            log_type: r["log_type"].as_str().unwrap_or("unknown").to_string(),
124            action: r["action"].as_str().map(|s| s.to_string()),
125            user_id: r["user_id"].as_str().map(|s| s.to_string()),
126            target: r["target_type_table_id"].as_str().map(|s| s.to_string()),
127        })
128        .collect();
129
130    Ok(entries)
131}