autoendpoint/routes/
health.rs1use std::collections::HashMap;
3use std::thread;
4
5use actix_web::{
6 web::{Data, Json},
7 HttpResponse,
8};
9use reqwest::StatusCode;
10use serde_json::json;
11
12use crate::error::{ApiErrorKind, ApiResult};
13use crate::server::AppState;
14use autopush_common::db::error::DbResult;
15#[cfg(feature = "reliable_report")]
16use autopush_common::errors::ApcError;
17#[cfg(feature = "reliable_report")]
18use autopush_common::metric_name::MetricName;
19#[cfg(feature = "reliable_report")]
20use autopush_common::metrics::StatsdClientExt;
21#[cfg(feature = "reliable_report")]
22use autopush_common::util::b64_encode_url;
23
24pub async fn health_route(state: Data<AppState>) -> Json<serde_json::Value> {
26 let router_health = interpret_table_health(state.db.router_table_exists().await);
27 let message_health = interpret_table_health(state.db.message_table_exists().await);
28 let mut routers: HashMap<&str, bool> = HashMap::new();
29 routers.insert("apns", state.apns_router.active());
30 routers.insert("fcm", state.fcm_router.active());
31
32 #[allow(unused_mut)]
34 let mut health = json!({
35 "status": if state
36 .db
37 .health_check()
38 .await
39 .map_err(|e| {
40 error!("Autoendpoint health error: {:?}", e);
41 e
42 })
43 .is_ok() {
44 "OK"
45 } else {
46 "ERROR"
47 },
48 "version": env!("CARGO_PKG_VERSION"),
49 "router_table": router_health,
50 "message_table": message_health,
51 "routers": routers,
52 });
53
54 #[cfg(feature = "reliable_report")]
55 {
56 let reliability_health: Result<String, ApcError> = state
57 .reliability
58 .health_check()
59 .await
60 .map(|_| {
61 let keys: Vec<String> = state
62 .settings
63 .tracking_keys()
64 .unwrap_or_default()
65 .iter()
66 .map(|k|
67 b64_encode_url(k)[..8].to_string())
69 .collect();
70 if keys.is_empty() {
71 Ok("NO_TRACKING_KEYS".to_owned())
72 } else {
73 Ok(format!("OK: {}", keys.join(",")))
74 }
75 })
76 .unwrap_or_else(|e| {
77 state
79 .metrics
80 .incr_with_tags(MetricName::ReliabilityErrorRedisUnavailable)
81 .with_tag("application", "autoendpoint")
82 .send();
83 error!("🔍🟥 Reliability reporting down: {:?}", e);
84 Ok("STORE_ERROR".to_owned())
85 });
86 health["reliability"] = json!(reliability_health);
87 }
88 Json(health)
89}
90
91fn interpret_table_health(health: DbResult<bool>) -> serde_json::Value {
93 match health {
94 Ok(true) => json!({
95 "status": "OK"
96 }),
97 Ok(false) => json!({
98 "status": "NOT OK",
99 "cause": "Nonexistent table"
100 }),
101 Err(e) => {
102 error!("Autoendpoint health error: {:?}", e);
103 json!({
104 "status": "NOT OK",
105 "cause": e.to_string()
106 })
107 }
108 }
109}
110
111pub async fn status_route() -> ApiResult<Json<serde_json::Value>> {
113 Ok(Json(json!({
114 "status": "OK",
115 "version": env!("CARGO_PKG_VERSION"),
116 })))
117}
118
119pub async fn lb_heartbeat_route() -> HttpResponse {
121 HttpResponse::Ok().finish()
123}
124
125pub async fn version_route() -> HttpResponse {
127 HttpResponse::Ok()
130 .content_type("application/json")
131 .body(include_str!("../../../version.json"))
132}
133
134pub async fn log_check() -> ApiResult<String> {
136 error!(
137 "Test Critical Message";
138 "status_code" => StatusCode::IM_A_TEAPOT.as_u16(),
139 "errno" => 999,
140 );
141
142 thread::spawn(|| {
143 panic!("LogCheck");
144 });
145
146 Err(ApiErrorKind::LogCheck.into())
147}