omni_orchestrator/schemas/v1/db/queries/audit_log.rs
1use crate::models::audit_log::AuditLog;
2use anyhow::Context;
3use sqlx::{MySql, Pool};
4
5/// Creates a new audit log entry in the system.
6///
7/// This function records an action performed within the system, tracking who
8/// performed the action, what organization they belong to, what action was
9/// performed, on what type of resource, and which specific resource was affected.
10/// It serves as a critical component for maintaining accountability and tracking
11/// system changes.
12///
13/// # Arguments
14///
15/// * `pool` - Database connection pool for executing the query
16/// * `user_id` - Optional ID of the user who performed the action (None for system actions)
17/// * `org_id` - Optional ID of the organization in which the action occurred
18/// * `action` - Description of the action performed (e.g., "create", "update", "delete")
19/// * `resource_type` - Type of resource affected (e.g., "app", "deployment", "user")
20/// * `resource_id` - Optional identifier of the specific resource affected
21///
22/// # Returns
23///
24/// * `Ok(AuditLog)` - Successfully created audit log entry with all database-assigned fields
25/// * `Err(anyhow::Error)` - Failed to create the audit log entry
26///
27/// # Examples
28///
29/// ```
30/// // Log a user creating an application
31/// create_audit_log(
32/// &pool,
33/// Some(user_id),
34/// Some(org_id),
35/// "create",
36/// "app",
37/// Some(app_id.to_string())
38/// ).await?;
39///
40/// // Log a system maintenance action
41/// create_audit_log(
42/// &pool,
43/// None,
44/// None,
45/// "maintenance",
46/// "system",
47/// None
48/// ).await?;
49/// ```
50pub async fn create_audit_log(
51 pool: &Pool<MySql>,
52 user_id: Option<i64>,
53 org_id: Option<i64>,
54 action: &str,
55 resource_type: &str,
56 resource_id: Option<String>,
57) -> anyhow::Result<AuditLog> {
58 let audit_log = sqlx::query_as::<_, AuditLog>(
59 r#"
60 INSERT INTO audit_logs (
61 user_id, org_id, action, resource_type, resource_id
62 ) VALUES (?, ?, ?, ?, ?)
63 "#,
64 )
65 .bind(user_id)
66 .bind(org_id)
67 .bind(action)
68 .bind(resource_type)
69 .bind(resource_id)
70 .fetch_one(pool)
71 .await
72 .context("Failed to create audit log")?;
73
74 Ok(audit_log)
75}
76
77/// Retrieves a paginated list of audit logs ordered by creation time.
78///
79/// This function fetches audit logs with pagination support, allowing for
80/// efficient browsing through potentially large numbers of log entries.
81/// The most recent logs are returned first.
82///
83/// # Arguments
84///
85/// * `pool` - Database connection pool for executing the query
86/// * `limit` - Maximum number of logs to retrieve in this page
87/// * `page` - Zero-based page offset (e.g., 0 for first page, 1 for second page)
88///
89/// # Returns
90///
91/// * `Ok(Vec<AuditLog>)` - Successfully retrieved audit logs for the requested page
92/// * `Err(anyhow::Error)` - Failed to fetch the audit logs
93///
94/// # Pagination
95///
96/// The function calculates the appropriate OFFSET based on the page and limit values.
97/// For example, with a limit of 10:
98/// - page 0 → entries 0-9
99/// - page 1 → entries 10-19
100/// - page 2 → entries 20-29
101///
102/// # Notes
103///
104/// The page parameter in this function represents the page number, not the offset value.
105/// The actual offset is calculated internally as `page * limit`.
106pub async fn list_audit_logs_paginated(
107 pool: &Pool<MySql>,
108 limit: i64,
109 page: i64,
110) -> anyhow::Result<Vec<AuditLog>> {
111 let audit_logs = sqlx::query_as::<_, AuditLog>(
112 r#"
113 SELECT * FROM audit_logs
114 ORDER BY created_at DESC
115 LIMIT ? OFFSET ?
116 "#,
117 )
118 .bind(limit)
119 .bind(page)
120 .fetch_all(pool)
121 .await
122 .context("Failed to fetch audit logs")?;
123
124 Ok(audit_logs)
125}
126
127/// Count the total number of audit logs in the system.
128///
129/// This function provides a simple count of all audit logs stored in the database.
130/// It is useful for understanding the volume of logged actions and can be
131/// used in conjunction with pagination to inform users about the
132/// total number of available logs.
133///
134/// # Arguments
135///
136/// * `pool` - Database connection pool for executing the query
137///
138/// # Returns
139///
140/// * `Ok(i64)` - Total number of audit logs in the system
141pub async fn count_audit_logs(
142 pool: &Pool<MySql>,
143) -> anyhow::Result<i64> {
144 let count = sqlx::query_scalar::<_, i64>(
145 r#"
146 SELECT COUNT(*) FROM audit_logs
147 "#,
148 )
149 .fetch_one(pool)
150 .await
151 .context("Failed to count audit logs")?;
152
153 Ok(count)
154}
155
156/// Retrieves audit logs for a specific resource.
157///
158/// This function fetches audit logs related to a particular resource, identified
159/// by its type and ID. This is useful for viewing the history of actions performed
160/// on a specific entity in the system.
161///
162/// # Arguments
163///
164/// * `pool` - Database connection pool for executing the query
165/// * `resource_type` - Type of resource to filter by (e.g., "app", "deployment")
166/// * `resource_id` - Identifier of the specific resource to get logs for
167/// * `limit` - Maximum number of logs to retrieve
168///
169/// # Returns
170///
171/// * `Ok(Vec<AuditLog>)` - Successfully retrieved audit logs for the resource
172/// * `Err(anyhow::Error)` - Failed to fetch the resource audit logs
173///
174/// # Ordering
175///
176/// Results are ordered by creation time in descending order, so the most
177/// recent actions appear first in the returned list.
178///
179/// # Example
180///
181/// ```
182/// // Get the most recent 20 actions on an application
183/// let app_history = get_audit_logs_by_resource(
184/// &pool,
185/// "app",
186/// &app_id.to_string(),
187/// 20
188/// ).await?;
189/// ```
190pub async fn get_audit_logs_by_resource(
191 pool: &Pool<MySql>,
192 resource_type: &str,
193 resource_id: &str,
194 limit: i64,
195) -> anyhow::Result<Vec<AuditLog>> {
196 let audit_logs = sqlx::query_as::<_, AuditLog>(
197 r#"
198 SELECT * FROM audit_logs
199 WHERE resource_type = ? AND resource_id = ?
200 ORDER BY created_at DESC
201 LIMIT ?
202 "#,
203 )
204 .bind(resource_type)
205 .bind(resource_id)
206 .bind(limit)
207 .fetch_all(pool)
208 .await
209 .context("Failed to fetch resource audit logs")?;
210
211 Ok(audit_logs)
212}
213
214/// Retrieves audit logs for actions performed by a specific user.
215///
216/// This function fetches audit logs for activities carried out by a particular user,
217/// identified by their user ID. This is useful for monitoring user activities,
218/// security auditing, or providing users with a history of their actions.
219///
220/// # Arguments
221///
222/// * `pool` - Database connection pool for executing the query
223/// * `user_id` - Unique identifier of the user whose logs to retrieve
224/// * `limit` - Maximum number of logs to retrieve
225///
226/// # Returns
227///
228/// * `Ok(Vec<AuditLog>)` - Successfully retrieved audit logs for the user
229/// * `Err(anyhow::Error)` - Failed to fetch the user audit logs
230///
231/// # Use Cases
232///
233/// Common use cases for this function include:
234/// - Security monitoring for suspicious user activity
235/// - User activity history displays in admin interfaces
236/// - Accountability tracking for administrative actions
237/// - Providing users with a history of their own actions in the system
238///
239/// # Ordering
240///
241/// Results are ordered by creation time in descending order, so the most
242/// recent actions appear first in the returned list.
243pub async fn get_user_audit_logs(
244 pool: &Pool<MySql>,
245 user_id: i64,
246 limit: i64,
247) -> anyhow::Result<Vec<AuditLog>> {
248 let audit_logs = sqlx::query_as::<_, AuditLog>(
249 r#"
250 SELECT * FROM audit_logs
251 WHERE user_id = ?
252 ORDER BY created_at DESC
253 LIMIT ?
254 "#,
255 )
256 .bind(user_id)
257 .bind(limit)
258 .fetch_all(pool)
259 .await
260 .context("Failed to fetch user audit logs")?;
261
262 Ok(audit_logs)
263}
264
265/// Retrieves audit logs for actions within a specific organization.
266///
267/// This function fetches audit logs for all activities that occurred within
268/// a particular organization, identified by its organization ID. This is useful
269/// for organizational-level auditing, compliance reporting, and activity monitoring.
270///
271/// # Arguments
272///
273/// * `pool` - Database connection pool for executing the query
274/// * `org_id` - Unique identifier of the organization whose logs to retrieve
275/// * `limit` - Maximum number of logs to retrieve
276///
277/// # Returns
278///
279/// * `Ok(Vec<AuditLog>)` - Successfully retrieved audit logs for the organization
280/// * `Err(anyhow::Error)` - Failed to fetch the organization audit logs
281///
282/// # Use Cases
283///
284/// Common use cases for this function include:
285/// - Compliance reporting for organization activities
286/// - Administrative oversight of organization-wide actions
287/// - Organizational security auditing
288/// - Activity dashboards for organization managers
289///
290/// # Ordering
291///
292/// Results are ordered by creation time in descending order, so the most
293/// recent actions appear first in the returned list.
294///
295/// # Note
296///
297/// This function retrieves all actions associated with the organization,
298/// regardless of which user performed them. This includes system actions
299/// that affect the organization but weren't initiated by a specific user.
300pub async fn get_org_audit_logs(
301 pool: &Pool<MySql>,
302 org_id: i64,
303 limit: i64,
304) -> anyhow::Result<Vec<AuditLog>> {
305 let audit_logs = sqlx::query_as::<_, AuditLog>(
306 r#"
307 SELECT * FROM audit_logs
308 WHERE org_id = ?
309 ORDER BY created_at DESC
310 LIMIT ?
311 "#,
312 )
313 .bind(org_id)
314 .bind(limit)
315 .fetch_all(pool)
316 .await
317 .context("Failed to fetch organization audit logs")?;
318
319 Ok(audit_logs)
320}
321
322/// Retrieves audit logs for a given app_id.
323///
324/// This function fetches audit logs related to a specific application,
325/// identified by its app_id. This is useful for viewing the history
326/// of actions performed on a specific application in the system.
327///
328/// # Arguments
329///
330/// * `pool` - Database connection pool for executing the query
331/// * `app_id` - Identifier of the specific application to get logs for
332/// * `page` - Zero-based page offset (e.g., 0 for first page, 1 for second page)
333/// * `per_page` - Maximum number of logs to retrieve
334///
335/// # Returns
336///
337/// * `Ok(Vec<AuditLog>)` - Successfully retrieved audit logs for the application
338pub async fn get_audit_logs_by_app(
339 pool: &Pool<MySql>,
340 app_id: i64,
341 page: i64,
342 per_page: i64,
343) -> anyhow::Result<Vec<AuditLog>> {
344 let audit_logs = sqlx::query_as::<_, AuditLog>(
345 r#"
346 SELECT * FROM audit_logs
347 WHERE app_id = ?
348 ORDER BY created_at DESC
349 LIMIT ? OFFSET ?
350 "#,
351 )
352 .bind(app_id)
353 .bind(per_page)
354 .bind(page * per_page)
355 .fetch_all(pool)
356 .await
357 .context("Failed to fetch app audit logs")?;
358
359 Ok(audit_logs)
360}
361
362/// Retrieves the number of audit logs for a given app_id.
363///
364/// This function counts the number of audit logs related to a specific application,
365/// identified by its app_id. This is useful for understanding the volume of logged
366/// actions for a specific application.
367///
368/// # Arguments
369/// * `pool` - Database connection pool for executing the query
370/// * `app_id` - Identifier of the specific application to count logs for
371///
372/// # Returns
373///
374/// * `Ok(i64)` - Total number of audit logs for the application
375pub async fn count_audit_logs_by_app(
376 pool: &Pool<MySql>,
377 app_id: i64,
378) -> anyhow::Result<i64> {
379 let count = sqlx::query_scalar::<_, i64>(
380 r#"
381 SELECT COUNT(*) FROM audit_logs
382 WHERE app_id = ?
383 "#,
384 )
385 .bind(app_id)
386 .fetch_one(pool)
387 .await
388 .context("Failed to count app audit logs")?;
389
390 Ok(count)
391}