1use std::sync::Arc;
9
10use anyhow::Result;
11use colored::Colorize;
12use serde::Serialize;
13use tokio::time::Duration;
14use tracing::info;
15
16use crate::api::{buscar_biblioteca, criar_cliente_http, executar_com_retry};
17use crate::i18n::{t, Mensagem};
18use crate::output::{emitir_ndjson, imprimir_linha_health, simbolo_health};
19use crate::storage::carregar_chaves_api;
20
21#[derive(Debug, Serialize)]
25pub struct RelatorioHealth {
26 pub config_ok: bool,
27 pub keys_count: usize,
28 pub api_alcancavel: bool,
29 pub detalhes_api: Option<String>,
30}
31
32pub async fn executar_health(json: bool) -> Result<i32> {
38 info!("Iniciando health check");
39
40 if !json {
41 imprimir_linha_health(&t(Mensagem::HealthExecutando).cyan().to_string());
42 }
43
44 let cliente = match criar_cliente_http() {
46 Ok(c) => {
47 if !json {
48 imprimir_linha_health(&format!(
49 "{} {}",
50 simbolo_health(true),
51 t(Mensagem::HealthConfigOk).green()
52 ));
53 }
54 c
55 }
56 Err(err) => {
57 let detalhe = err.to_string();
58 if json {
59 let relatorio = RelatorioHealth {
60 config_ok: false,
61 keys_count: 0,
62 api_alcancavel: false,
63 detalhes_api: Some(detalhe.clone()),
64 };
65 emitir_ndjson("health", &relatorio);
66 } else {
67 imprimir_linha_health(&format!(
68 "{} {} — {}",
69 simbolo_health(false),
70 t(Mensagem::HealthConfigFalhou).red(),
71 detalhe
72 ));
73 }
74 return Ok(74);
75 }
76 };
77
78 let chaves = match carregar_chaves_api() {
80 Ok(c) if !c.is_empty() => {
81 if !json {
82 imprimir_linha_health(&format!(
83 "{} {} {}",
84 simbolo_health(true),
85 t(Mensagem::HealthKeysOk).green(),
86 c.len().to_string().bold()
87 ));
88 }
89 c
90 }
91 _ => {
92 if json {
93 let relatorio = RelatorioHealth {
94 config_ok: true,
95 keys_count: 0,
96 api_alcancavel: false,
97 detalhes_api: None,
98 };
99 emitir_ndjson("health", &relatorio);
100 } else {
101 imprimir_linha_health(&format!(
102 "{} {}",
103 simbolo_health(false),
104 t(Mensagem::HealthKeysFaltando).yellow()
105 ));
106 }
107 return Ok(66);
108 }
109 };
110
111 let cliente_arc = Arc::new(cliente);
113 let probe_result = tokio::time::timeout(
114 Duration::from_secs(10),
115 executar_com_retry(&chaves, move |chave| {
116 let c = Arc::clone(&cliente_arc);
117 async move { buscar_biblioteca(&c, &chave, "react", "health probe").await }
118 }),
119 )
120 .await;
121
122 let api_alcancavel = matches!(probe_result, Ok(Ok(_)));
123 let detalhes_api = match &probe_result {
124 Err(_) => Some("timeout after 10s".to_string()),
125 Ok(Err(e)) => Some(e.to_string()),
126 Ok(Ok(_)) => None,
127 };
128
129 if json {
130 let relatorio = RelatorioHealth {
131 config_ok: true,
132 keys_count: chaves.len(),
133 api_alcancavel,
134 detalhes_api: detalhes_api.clone(),
135 };
136 emitir_ndjson("health", &relatorio);
137 } else if api_alcancavel {
138 imprimir_linha_health(&format!(
139 "{} {}",
140 simbolo_health(true),
141 t(Mensagem::HealthApiOk).green()
142 ));
143 } else {
144 let detalhe = detalhes_api.as_deref().unwrap_or("");
145 imprimir_linha_health(&format!(
146 "{} {} — {}",
147 simbolo_health(false),
148 t(Mensagem::HealthApiOffline).red(),
149 detalhe
150 ));
151 }
152
153 if api_alcancavel {
154 Ok(0)
155 } else {
156 Ok(69)
157 }
158}