1use 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; #[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#[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 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 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 let page = page.unwrap_or(0);
128 let per_page = per_page.unwrap_or(20);
129
130 let status_ref = status.as_deref();
132 let severity_ref = severity.as_deref();
133 let service_ref = service.as_deref();
134
135 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 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("/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 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 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#[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 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 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#[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, db_manager: &State<Arc<DatabaseManager>>,
333) -> Result<Json<Value>, (Status, Json<Value>)> {
334 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 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 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#[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, db_manager: &State<Arc<DatabaseManager>>,
420) -> Result<Json<Value>, (Status, Json<Value>)> {
421 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 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#[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, db_manager: &State<Arc<DatabaseManager>>,
493) -> Result<Json<Value>, (Status, Json<Value>)> {
494 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 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 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#[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 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 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("/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 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 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("/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 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 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("/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 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 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); 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("/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 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 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); 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#[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>>, db_manager: &State<Arc<DatabaseManager>>,
878) -> Result<Json<Value>, (Status, Json<Value>)> {
879 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 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); 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#[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 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 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#[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, db_manager: &State<Arc<DatabaseManager>>,
1037) -> Result<Json<Value>, (Status, Json<Value>)> {
1038 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 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 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 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, 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}