omni_orchestrator/schemas/v1/api/
alerts.rs

1//! Alert management module for handling CRUD operations on alerts.
2//! 
3//! This module provides functionality to create, read, update, and delete
4//! alerts in the system. It includes endpoints for managing alerts
5//! associated with applications and organizations.
6
7use crate::models::alert::{
8    Alert, AlertWithRelatedData, AlertAcknowledgment, AlertEscalation
9};
10use super::super::db::queries as db;
11use chrono::{DateTime, Utc};
12use rocket::http::Status;
13use rocket::serde::json::{json, Json, Value};
14use rocket::{delete, get, http::ContentType, post, put, State};
15use serde::{Deserialize, Serialize};
16use rocket::time::OffsetDateTime;
17use std::collections::HashMap;
18use std::sync::Arc;
19use crate::DatabaseManager;
20use crate::models::user::User; // Add this at the top if not already present
21
22// Request and response structs
23
24#[derive(Debug, Serialize, Deserialize)]
25pub struct CreateAlertRequest {
26    alert_type: String,
27    severity: String,
28    service: String,
29    message: String,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    metadata: Option<serde_json::Value>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    org_id: Option<i64>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    app_id: Option<i64>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    instance_id: Option<i64>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    region_id: Option<i64>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    node_id: Option<i64>,
42}
43
44#[derive(Debug, Serialize, Deserialize)]
45pub struct UpdateAlertStatusRequest {
46    status: String,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    notes: Option<String>,
49}
50
51#[derive(Debug, Serialize, Deserialize)]
52pub struct AcknowledgeAlertRequest {
53    #[serde(skip_serializing_if = "Option::is_none")]
54    notes: Option<String>,
55    #[serde(default)]
56    update_status: bool,
57}
58
59#[derive(Debug, Serialize, Deserialize)]
60pub struct CreateEscalationRequest {
61    escalation_level: i64,
62    escalated_to: serde_json::Value,
63    escalation_method: String,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    response_required_by: Option<DateTime<Utc>>,
66}
67
68#[derive(Debug, Serialize, Deserialize)]
69pub struct BulkUpdateStatusRequest {
70    #[serde(skip_serializing_if = "Option::is_none")]
71    ids: Option<Vec<i64>>,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    service: Option<String>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    app_id: Option<i64>,
76    status: String,
77    #[serde(skip_serializing_if = "Option::is_none")]
78    notes: Option<String>,
79}
80
81// API Routes
82
83/// Get a paginated list of alerts with filtering options
84#[get("/platform/<platform_id>/alerts?<page>&<per_page>&<status>&<severity>&<org_id>&<app_id>&<service>&<from_date>&<to_date>")]
85pub async fn list_alerts(
86    platform_id: i64,
87    page: Option<i64>,
88    per_page: Option<i64>,
89    status: Option<String>,
90    severity: Option<String>,
91    org_id: Option<i64>,
92    app_id: Option<i64>,
93    service: Option<String>,
94    from_date: Option<String>,
95    to_date: Option<String>,
96    db_manager: &State<Arc<DatabaseManager>>,
97) -> Result<Json<Value>, (Status, Json<Value>)> {
98    // Get platform information
99    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
100        Ok(platform) => platform,
101        Err(_) => {
102            return Err((
103                Status::NotFound,
104                Json(json!({
105                    "error": "Platform not found",
106                    "message": format!("Platform with ID {} does not exist", platform_id)
107                }))
108            ));
109        }
110    };
111
112    // Get platform-specific database pool
113    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
114        Ok(pool) => pool,
115        Err(_) => {
116            return Err((
117                Status::InternalServerError,
118                Json(json!({
119                    "error": "Database error",
120                    "message": "Failed to connect to platform database"
121                }))
122            ));
123        }
124    };
125
126    // Set default pagination if not provided
127    let page = page.unwrap_or(0);
128    let per_page = per_page.unwrap_or(20);
129    
130    // Convert Optional String to Optional &str
131    let status_ref = status.as_deref();
132    let severity_ref = severity.as_deref();
133    let service_ref = service.as_deref();
134
135    // Fetch alerts with filters
136    let alerts = match db::alert::list_alerts(
137        &pool, 
138        page, 
139        per_page,
140        status_ref,
141        severity_ref,
142        org_id,
143        app_id,
144        service_ref,
145        from_date.and_then(|date_str| chrono::DateTime::parse_from_rfc3339(&date_str).ok().map(|dt| dt.with_timezone(&chrono::Utc))),
146        to_date.and_then(|date_str| chrono::DateTime::parse_from_rfc3339(&date_str).ok().map(|dt| dt.with_timezone(&chrono::Utc))),
147    ).await {
148        Ok(alerts) => alerts,
149        Err(e) => {
150            log::error!("Failed to fetch alerts: {}", e);
151            return Err((
152                Status::InternalServerError,
153                Json(json!({
154                    "error": "Database error",
155                    "message": "Failed to fetch alerts"
156                }))
157            ));
158        }
159    };
160
161    // Count total alerts with same filters for pagination
162    let total_count = match db::alert::count_alerts(
163        &pool,
164        status_ref,
165        severity_ref,
166        org_id,
167        app_id,
168    ).await {
169        Ok(count) => count,
170        Err(e) => {
171            log::error!("Failed to fetch alert count: {}", e);
172            return Err((
173                Status::InternalServerError,
174                Json(json!({
175                    "error": "Database error",
176                    "message": "Failed to count alerts"
177                }))
178            ));
179        }
180    };
181
182    let total_pages = (total_count as f64 / per_page as f64).ceil() as i64;
183    let response = json!({
184        "alerts": alerts,
185        "pagination": {
186            "page": page,
187            "per_page": per_page,
188            "total_count": total_count,
189            "total_pages": total_pages
190        }
191    });
192
193    Ok(Json(response))
194}
195
196/// Get details of a specific alert including related data
197#[get("/platform/<platform_id>/alerts/<id>")]
198pub async fn get_alert(
199    platform_id: i64,
200    id: i64,
201    db_manager: &State<Arc<DatabaseManager>>,
202) -> Result<Json<Value>, (Status, Json<Value>)> {
203    // Get platform information
204    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
205        Ok(platform) => platform,
206        Err(_) => {
207            return Err((
208                Status::NotFound,
209                Json(json!({
210                    "error": "Platform not found",
211                    "message": format!("Platform with ID {} does not exist", platform_id)
212                }))
213            ));
214        }
215    };
216
217    // Get platform-specific database pool
218    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
219        Ok(pool) => pool,
220        Err(_) => {
221            return Err((
222                Status::InternalServerError,
223                Json(json!({
224                    "error": "Database error",
225                    "message": "Failed to connect to platform database"
226                }))
227            ));
228        }
229    };
230
231    let alert_data = match db::alert::get_alert_with_related_data(&pool, id).await {
232        Ok(data) => data,
233        Err(e) => {
234            log::error!("Failed to fetch alert {}: {}", id, e);
235            return Err((
236                if e.to_string().contains("no rows") {
237                    Status::NotFound
238                } else {
239                    Status::InternalServerError
240                },
241                Json(json!({
242                    "error": if e.to_string().contains("no rows") { "Alert not found" } else { "Database error" },
243                    "message": if e.to_string().contains("no rows") { 
244                        format!("Alert with ID {} does not exist", id) 
245                    } else { 
246                        "Failed to fetch alert details".to_string() 
247                    }
248                }))
249            ));
250        }
251    };
252
253    Ok(Json(json!(alert_data)))
254}
255
256/// Create a new alert
257#[post("/platform/<platform_id>/alerts", format = "json", data = "<alert_data>")]
258pub async fn create_alert(
259    platform_id: i64,
260    alert_data: Json<CreateAlertRequest>,
261    db_manager: &State<Arc<DatabaseManager>>,
262) -> Result<Json<Value>, (Status, Json<Value>)> {
263    // Get platform information
264    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
265        Ok(platform) => platform,
266        Err(_) => {
267            return Err((
268                Status::NotFound,
269                Json(json!({
270                    "error": "Platform not found",
271                    "message": format!("Platform with ID {} does not exist", platform_id)
272                }))
273            ));
274        }
275    };
276
277    // Get platform-specific database pool
278    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
279        Ok(pool) => pool,
280        Err(_) => {
281            return Err((
282                Status::InternalServerError,
283                Json(json!({
284                    "error": "Database error",
285                    "message": "Failed to connect to platform database"
286                }))
287            ));
288        }
289    };
290
291    let data = alert_data.into_inner();
292    
293    let alert = match db::alert::create_alert(
294        &pool,
295        &data.alert_type,
296        &data.severity,
297        &data.service,
298        &data.message,
299        data.metadata,
300        data.org_id,
301        data.app_id,
302        data.instance_id,
303        data.region_id,
304        data.node_id,
305    ).await {
306        Ok(alert) => alert,
307        Err(e) => {
308            log::error!("Failed to create alert: {}", e);
309            return Err((
310                Status::InternalServerError,
311                Json(json!({
312                    "error": "Database error",
313                    "message": "Failed to create alert"
314                }))
315            ));
316        }
317    };
318
319    Ok(Json(json!({
320        "message": "Alert created successfully",
321        "alert": alert
322    })))
323}
324
325/// Update an alert's status
326#[put("/platform/<platform_id>/alerts/<id>/status", format = "json", data = "<status_data>")]
327pub async fn update_alert_status(
328    platform_id: i64,
329    id: i64,
330    status_data: Json<UpdateAlertStatusRequest>,
331    user: User, // Extract user from request guard
332    db_manager: &State<Arc<DatabaseManager>>,
333) -> Result<Json<Value>, (Status, Json<Value>)> {
334    // Get platform information
335    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
336        Ok(platform) => platform,
337        Err(_) => {
338            return Err((
339                Status::NotFound,
340                Json(json!({
341                    "error": "Platform not found",
342                    "message": format!("Platform with ID {} does not exist", platform_id)
343                }))
344            ));
345        }
346    };
347
348    // Get platform-specific database pool
349    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
350        Ok(pool) => pool,
351        Err(_) => {
352            return Err((
353                Status::InternalServerError,
354                Json(json!({
355                    "error": "Database error",
356                    "message": "Failed to connect to platform database"
357                }))
358            ));
359        }
360    };
361
362    let data = status_data.into_inner();
363    
364    // Validate the status is a valid value
365    match data.status.as_str() {
366        "active" | "acknowledged" | "resolved" | "auto_resolved" => {},
367        _ => return Err((
368            Status::BadRequest,
369            Json(json!({
370                "error": "Invalid status",
371                "message": "Status must be one of: active, acknowledged, resolved, auto_resolved"
372            }))
373        ))
374    }
375    
376    let user_id = user.id;
377
378    let updated_alert = match db::alert::update_alert_status(
379        &pool,
380        id,
381        &data.status,
382        Some(user_id),
383        data.notes.as_deref(),
384    ).await {
385        Ok(alert) => alert,
386        Err(e) => {
387            log::error!("Failed to update alert status: {}", e);
388            return Err((
389                if e.to_string().contains("no rows") {
390                    Status::NotFound
391                } else {
392                    Status::InternalServerError
393                },
394                Json(json!({
395                    "error": if e.to_string().contains("no rows") { "Alert not found" } else { "Database error" },
396                    "message": if e.to_string().contains("no rows") { 
397                        format!("Alert with ID {} does not exist", id) 
398                    } else { 
399                        "Failed to update alert status".to_string() 
400                    }
401                }))
402            ));
403        }
404    };
405
406    Ok(Json(json!({
407        "message": "Alert status updated successfully",
408        "alert": updated_alert
409    })))
410}
411
412/// Acknowledge an alert
413#[post("/platform/<platform_id>/alerts/<id>/acknowledge", format = "json", data = "<ack_data>")]
414pub async fn acknowledge_alert(
415    platform_id: i64,
416    id: i64,
417    ack_data: Json<AcknowledgeAlertRequest>,
418    user: User, // Extract user from request guard
419    db_manager: &State<Arc<DatabaseManager>>,
420) -> Result<Json<Value>, (Status, Json<Value>)> {
421    // Get platform information
422    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
423        Ok(platform) => platform,
424        Err(_) => {
425            return Err((
426                Status::NotFound,
427                Json(json!({
428                    "error": "Platform not found",
429                    "message": format!("Platform with ID {} does not exist", platform_id)
430                }))
431            ));
432        }
433    };
434
435    // Get platform-specific database pool
436    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
437        Ok(pool) => pool,
438        Err(_) => {
439            return Err((
440                Status::InternalServerError,
441                Json(json!({
442                    "error": "Database error",
443                    "message": "Failed to connect to platform database"
444                }))
445            ));
446        }
447    };
448
449    let data = ack_data.into_inner();
450    
451    let acknowledgment = match db::alert::acknowledge_alert(
452        &pool,
453        id,
454        user.id,
455        data.notes.as_deref(),
456        data.update_status,
457    ).await {
458        Ok(ack) => ack,
459        Err(e) => {
460            log::error!("Failed to acknowledge alert: {}", e);
461            return Err((
462                if e.to_string().contains("no rows") {
463                    Status::NotFound
464                } else {
465                    Status::InternalServerError
466                },
467                Json(json!({
468                    "error": if e.to_string().contains("no rows") { "Alert not found" } else { "Database error" },
469                    "message": if e.to_string().contains("no rows") { 
470                        format!("Alert with ID {} does not exist", id) 
471                    } else { 
472                        "Failed to acknowledge alert".to_string() 
473                    }
474                }))
475            ));
476        }
477    };
478
479    Ok(Json(json!({
480        "message": "Alert acknowledged successfully",
481        "acknowledgment": acknowledgment
482    })))
483}
484
485/// Resolve an alert
486#[post("/platform/<platform_id>/alerts/<id>/resolve", format = "json", data = "<resolve_data>")]
487pub async fn resolve_alert(
488    platform_id: i64,
489    id: i64,
490    resolve_data: Option<Json<HashMap<String, String>>>,
491    user: User, // Extract user from request guard
492    db_manager: &State<Arc<DatabaseManager>>,
493) -> Result<Json<Value>, (Status, Json<Value>)> {
494    // Get platform information
495    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
496        Ok(platform) => platform,
497        Err(_) => {
498            return Err((
499                Status::NotFound,
500                Json(json!({
501                    "error": "Platform not found",
502                    "message": format!("Platform with ID {} does not exist", platform_id)
503                }))
504            ));
505        }
506    };
507
508    // Get platform-specific database pool
509    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
510        Ok(pool) => pool,
511        Err(_) => {
512            return Err((
513                Status::InternalServerError,
514                Json(json!({
515                    "error": "Database error",
516                    "message": "Failed to connect to platform database"
517                }))
518            ));
519        }
520    };
521
522    // Extract notes if provided
523    let notes = resolve_data
524        .and_then(|data| data.get("notes").cloned());
525    
526    let resolved_alert = match db::alert::resolve_alert(
527        &pool,
528        id,
529        user.id,
530        notes.as_deref(),
531    ).await {
532        Ok(alert) => alert,
533        Err(e) => {
534            log::error!("Failed to resolve alert: {}", e);
535            return Err((
536                if e.to_string().contains("no rows") {
537                    Status::NotFound
538                } else {
539                    Status::InternalServerError
540                },
541                Json(json!({
542                    "error": if e.to_string().contains("no rows") { "Alert not found" } else { "Database error" },
543                    "message": if e.to_string().contains("no rows") { 
544                        format!("Alert with ID {} does not exist", id) 
545                    } else { 
546                        "Failed to resolve alert".to_string() 
547                    }
548                }))
549            ));
550        }
551    };
552
553    Ok(Json(json!({
554        "message": "Alert resolved successfully",
555        "alert": resolved_alert
556    })))
557}
558
559/// Create an escalation for an alert
560#[post("/platform/<platform_id>/alerts/<id>/escalate", format = "json", data = "<escalation_data>")]
561pub async fn escalate_alert(
562    platform_id: i64,
563    id: i64,
564    escalation_data: Json<CreateEscalationRequest>,
565    db_manager: &State<Arc<DatabaseManager>>,
566) -> Result<Json<Value>, (Status, Json<Value>)> {
567    // Get platform information
568    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
569        Ok(platform) => platform,
570        Err(_) => {
571            return Err((
572                Status::NotFound,
573                Json(json!({
574                    "error": "Platform not found",
575                    "message": format!("Platform with ID {} does not exist", platform_id)
576                }))
577            ));
578        }
579    };
580
581    // Get platform-specific database pool
582    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
583        Ok(pool) => pool,
584        Err(_) => {
585            return Err((
586                Status::InternalServerError,
587                Json(json!({
588                    "error": "Database error",
589                    "message": "Failed to connect to platform database"
590                }))
591            ));
592        }
593    };
594
595    let data = escalation_data.into_inner();
596    
597    let escalation = match db::alert::create_alert_escalation(
598        &pool,
599        id,
600        data.escalation_level,
601        data.escalated_to,
602        &data.escalation_method,
603        data.response_required_by,
604    ).await {
605        Ok(esc) => esc,
606        Err(e) => {
607            log::error!("Failed to escalate alert: {}", e);
608            return Err((
609                if e.to_string().contains("no rows") {
610                    Status::NotFound
611                } else {
612                    Status::InternalServerError
613                },
614                Json(json!({
615                    "error": if e.to_string().contains("no rows") { "Alert not found" } else { "Database error" },
616                    "message": if e.to_string().contains("no rows") { 
617                        format!("Alert with ID {} does not exist", id) 
618                    } else { 
619                        "Failed to escalate alert".to_string() 
620                    }
621                }))
622            ));
623        }
624    };
625
626    Ok(Json(json!({
627        "message": "Alert escalated successfully",
628        "escalation": escalation
629    })))
630}
631
632/// Get alerts for a specific application
633#[get("/platform/<platform_id>/apps/<app_id>/alerts?<limit>&<include_resolved>")]
634pub async fn get_app_alerts(
635    platform_id: i64,
636    app_id: i64,
637    limit: Option<i64>,
638    include_resolved: Option<bool>,
639    db_manager: &State<Arc<DatabaseManager>>,
640) -> Result<Json<Value>, (Status, Json<Value>)> {
641    // Get platform information
642    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
643        Ok(platform) => platform,
644        Err(_) => {
645            return Err((
646                Status::NotFound,
647                Json(json!({
648                    "error": "Platform not found",
649                    "message": format!("Platform with ID {} does not exist", platform_id)
650                }))
651            ));
652        }
653    };
654
655    // Get platform-specific database pool
656    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
657        Ok(pool) => pool,
658        Err(_) => {
659            return Err((
660                Status::InternalServerError,
661                Json(json!({
662                    "error": "Database error",
663                    "message": "Failed to connect to platform database"
664                }))
665            ));
666        }
667    };
668
669    let limit = limit.unwrap_or(20);
670    let include_resolved = include_resolved.unwrap_or(false);
671    
672    let alerts = match db::alert::get_recent_app_alerts(
673        &pool,
674        app_id,
675        limit,
676        include_resolved,
677    ).await {
678        Ok(alerts) => alerts,
679        Err(e) => {
680            log::error!("Failed to fetch app alerts: {}", e);
681            return Err((
682                Status::InternalServerError,
683                Json(json!({
684                    "error": "Database error",
685                    "message": "Failed to fetch application alerts"
686                }))
687            ));
688        }
689    };
690
691    Ok(Json(json!({ "alerts": alerts })))
692}
693
694/// Get active alerts for an organization
695#[get("/platform/<platform_id>/orgs/<org_id>/active-alerts?<limit>")]
696pub async fn get_org_active_alerts(
697    platform_id: i64,
698    org_id: i64,
699    limit: Option<i64>,
700    db_manager: &State<Arc<DatabaseManager>>,
701) -> Result<Json<Value>, (Status, Json<Value>)> {
702    // Get platform information
703    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
704        Ok(platform) => platform,
705        Err(_) => {
706            return Err((
707                Status::NotFound,
708                Json(json!({
709                    "error": "Platform not found",
710                    "message": format!("Platform with ID {} does not exist", platform_id)
711                }))
712            ));
713        }
714    };
715
716    // Get platform-specific database pool
717    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
718        Ok(pool) => pool,
719        Err(_) => {
720            return Err((
721                Status::InternalServerError,
722                Json(json!({
723                    "error": "Database error",
724                    "message": "Failed to connect to platform database"
725                }))
726            ));
727        }
728    };
729
730    let limit = limit.unwrap_or(20);
731    
732    let alerts = match db::alert::get_org_active_alerts(
733        &pool,
734        org_id,
735        limit,
736    ).await {
737        Ok(alerts) => alerts,
738        Err(e) => {
739            log::error!("Failed to fetch org active alerts: {}", e);
740            return Err((
741                Status::InternalServerError,
742                Json(json!({
743                    "error": "Database error",
744                    "message": "Failed to fetch organization alerts"
745                }))
746            ));
747        }
748    };
749
750    Ok(Json(json!({ "alerts": alerts })))
751}
752
753/// Get alert statistics for an organization
754#[get("/platform/<platform_id>/orgs/<org_id>/alert-stats?<days>")]
755pub async fn get_org_alert_stats(
756    platform_id: i64,
757    org_id: i64,
758    days: Option<i64>,
759    db_manager: &State<Arc<DatabaseManager>>,
760) -> Result<Json<Value>, (Status, Json<Value>)> {
761    // Get platform information
762    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
763        Ok(platform) => platform,
764        Err(_) => {
765            return Err((
766                Status::NotFound,
767                Json(json!({
768                    "error": "Platform not found",
769                    "message": format!("Platform with ID {} does not exist", platform_id)
770                }))
771            ));
772        }
773    };
774
775    // Get platform-specific database pool
776    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
777        Ok(pool) => pool,
778        Err(_) => {
779            return Err((
780                Status::InternalServerError,
781                Json(json!({
782                    "error": "Database error",
783                    "message": "Failed to connect to platform database"
784                }))
785            ));
786        }
787    };
788
789    let days = days.unwrap_or(30); // Default to last 30 days
790    
791    let stats = match db::alert::get_alert_stats(
792        &pool,
793        org_id,
794        days,
795    ).await {
796        Ok(stats) => stats,
797        Err(e) => {
798            log::error!("Failed to fetch alert stats: {}", e);
799            return Err((
800                Status::InternalServerError,
801                Json(json!({
802                    "error": "Database error",
803                    "message": "Failed to fetch alert statistics"
804                }))
805            ));
806        }
807    };
808
809    Ok(Json(stats))
810}
811
812/// Get alerts needing escalation
813#[get("/platform/<platform_id>/alerts/needing-escalation?<org_id>&<hours_threshold>")]
814pub async fn get_alerts_needing_escalation(
815    platform_id: i64,
816    org_id: Option<i64>,
817    hours_threshold: Option<i64>,
818    db_manager: &State<Arc<DatabaseManager>>,
819) -> Result<Json<Value>, (Status, Json<Value>)> {
820    // Get platform information
821    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
822        Ok(platform) => platform,
823        Err(_) => {
824            return Err((
825                Status::NotFound,
826                Json(json!({
827                    "error": "Platform not found",
828                    "message": format!("Platform with ID {} does not exist", platform_id)
829                }))
830            ));
831        }
832    };
833
834    // Get platform-specific database pool
835    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
836        Ok(pool) => pool,
837        Err(_) => {
838            return Err((
839                Status::InternalServerError,
840                Json(json!({
841                    "error": "Database error",
842                    "message": "Failed to connect to platform database"
843                }))
844            ));
845        }
846    };
847
848    let hours_threshold = hours_threshold.unwrap_or(4); // Default to 4 hours
849    
850    let alerts = match db::alert::get_alerts_needing_escalation(
851        &pool,
852        org_id,
853        hours_threshold,
854    ).await {
855        Ok(alerts) => alerts,
856        Err(e) => {
857            log::error!("Failed to fetch alerts needing escalation: {}", e);
858            return Err((
859                Status::InternalServerError,
860                Json(json!({
861                    "error": "Database error",
862                    "message": "Failed to fetch alerts needing escalation"
863                }))
864            ));
865        }
866    };
867
868    Ok(Json(json!({ "alerts": alerts })))
869}
870
871/// Auto-resolve old alerts
872#[post("/platform/<platform_id>/alerts/auto-resolve?<days_threshold>&<severity_level>")]
873pub async fn auto_resolve_old_alerts(
874    platform_id: i64,
875    days_threshold: Option<i64>,
876    severity_level: Option<Vec<String>>, // Can provide multiple severity levels
877    db_manager: &State<Arc<DatabaseManager>>,
878) -> Result<Json<Value>, (Status, Json<Value>)> {
879    // Get platform information
880    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
881        Ok(platform) => platform,
882        Err(_) => {
883            return Err((
884                Status::NotFound,
885                Json(json!({
886                    "error": "Platform not found",
887                    "message": format!("Platform with ID {} does not exist", platform_id)
888                }))
889            ));
890        }
891    };
892
893    // Get platform-specific database pool
894    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
895        Ok(pool) => pool,
896        Err(_) => {
897            return Err((
898                Status::InternalServerError,
899                Json(json!({
900                    "error": "Database error",
901                    "message": "Failed to connect to platform database"
902                }))
903            ));
904        }
905    };
906
907    let days_threshold = days_threshold.unwrap_or(7); // Default to 7 days
908    
909    // Convert Vec<String> to Vec<&str>
910    let severity_refs: Option<Vec<&str>> = severity_level
911        .as_ref()
912        .map(|levels| levels.iter().map(AsRef::as_ref).collect());
913    
914    let count = match db::alert::auto_resolve_old_alerts(
915        &pool,
916        days_threshold,
917        severity_refs,
918    ).await {
919        Ok(count) => count,
920        Err(e) => {
921            log::error!("Failed to auto-resolve old alerts: {}", e);
922            return Err((
923                Status::InternalServerError,
924                Json(json!({
925                    "error": "Database error",
926                    "message": "Failed to auto-resolve old alerts"
927                }))
928            ));
929        }
930    };
931
932    Ok(Json(json!({
933        "message": "Successfully auto-resolved old alerts",
934        "count": count
935    })))
936}
937
938/// Search for alerts
939#[get("/platform/<platform_id>/alerts/search?<query>&<org_id>&<page>&<per_page>")]
940pub async fn search_alerts(
941    platform_id: i64,
942    query: String,
943    org_id: Option<i64>,
944    page: Option<i64>,
945    per_page: Option<i64>,
946    db_manager: &State<Arc<DatabaseManager>>,
947) -> Result<Json<Value>, (Status, Json<Value>)> {
948    // Get platform information
949    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
950        Ok(platform) => platform,
951        Err(_) => {
952            return Err((
953                Status::NotFound,
954                Json(json!({
955                    "error": "Platform not found",
956                    "message": format!("Platform with ID {} does not exist", platform_id)
957                }))
958            ));
959        }
960    };
961
962    // Get platform-specific database pool
963    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
964        Ok(pool) => pool,
965        Err(_) => {
966            return Err((
967                Status::InternalServerError,
968                Json(json!({
969                    "error": "Database error",
970                    "message": "Failed to connect to platform database"
971                }))
972            ));
973        }
974    };
975
976    let page = page.unwrap_or(0);
977    let per_page = per_page.unwrap_or(20);
978    
979    let alerts = match db::alert::search_alerts(
980        &pool,
981        &query,
982        org_id,
983        page,
984        per_page,
985    ).await {
986        Ok(alerts) => alerts,
987        Err(e) => {
988            log::error!("Failed to search alerts: {}", e);
989            return Err((
990                Status::InternalServerError,
991                Json(json!({
992                    "error": "Database error",
993                    "message": "Failed to search alerts"
994                }))
995            ));
996        }
997    };
998    
999    let total_count = match db::alert::count_search_alerts(
1000        &pool,
1001        &query,
1002        org_id,
1003    ).await {
1004        Ok(count) => count,
1005        Err(e) => {
1006            log::error!("Failed to count search results: {}", e);
1007            return Err((
1008                Status::InternalServerError,
1009                Json(json!({
1010                    "error": "Database error",
1011                    "message": "Failed to count search results"
1012                }))
1013            ));
1014        }
1015    };
1016    
1017    let total_pages = (total_count as f64 / per_page as f64).ceil() as i64;
1018    
1019    Ok(Json(json!({
1020        "alerts": alerts,
1021        "pagination": {
1022            "page": page,
1023            "per_page": per_page,
1024            "total_count": total_count,
1025            "total_pages": total_pages
1026        }
1027    })))
1028}
1029
1030/// Bulk update alert status
1031#[put("/platform/<platform_id>/alerts/bulk-status", format = "json", data = "<update_data>")]
1032pub async fn bulk_update_alert_status(
1033    platform_id: i64,
1034    update_data: Json<BulkUpdateStatusRequest>,
1035    user: User, // Extract user from request guard
1036    db_manager: &State<Arc<DatabaseManager>>,
1037) -> Result<Json<Value>, (Status, Json<Value>)> {
1038    // Get platform information
1039    let platform = match db::platforms::get_platform_by_id(db_manager.get_main_pool(), platform_id).await {
1040        Ok(platform) => platform,
1041        Err(_) => {
1042            return Err((
1043                Status::NotFound,
1044                Json(json!({
1045                    "error": "Platform not found",
1046                    "message": format!("Platform with ID {} does not exist", platform_id)
1047                }))
1048            ));
1049        }
1050    };
1051
1052    // Get platform-specific database pool
1053    let pool = match db_manager.get_platform_pool(&platform.name, platform_id).await {
1054        Ok(pool) => pool,
1055        Err(_) => {
1056            return Err((
1057                Status::InternalServerError,
1058                Json(json!({
1059                    "error": "Database error",
1060                    "message": "Failed to connect to platform database"
1061                }))
1062            ));
1063        }
1064    };
1065
1066    let data = update_data.into_inner();
1067    
1068    // Validate the status is a valid value
1069    match data.status.as_str() {
1070        "active" | "acknowledged" | "resolved" | "auto_resolved" => {},
1071        _ => return Err((
1072            Status::BadRequest,
1073            Json(json!({
1074                "error": "Invalid status",
1075                "message": "Status must be one of: active, acknowledged, resolved, auto_resolved"
1076            }))
1077        ))
1078    }
1079    
1080    // Validate that at least one filter is provided
1081    if data.ids.is_none() && data.service.is_none() && data.app_id.is_none() {
1082        return Err((
1083            Status::BadRequest,
1084            Json(json!({
1085                "error": "Missing filters",
1086                "message": "At least one filter (ids, service, or app_id) must be provided"
1087            }))
1088        ));
1089    }
1090
1091    let count = match db::alert::bulk_update_alert_status(
1092        &pool,
1093        data.ids,
1094        data.service.as_deref(),
1095        data.app_id,
1096        &data.status,
1097        user.id, // Use user.id instead of user_id
1098        data.notes.as_deref(),
1099    ).await {
1100        Ok(count) => count,
1101        Err(e) => {
1102            log::error!("Failed to bulk update alert status: {}", e);
1103            return Err((
1104                Status::InternalServerError,
1105                Json(json!({
1106                    "error": "Database error",
1107                    "message": "Failed to update alert statuses"
1108                }))
1109            ));
1110        }
1111    };
1112
1113    Ok(Json(json!({
1114        "message": "Successfully updated alert status",
1115        "count": count
1116    })))
1117}