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