diff --git a/Cargo.lock b/Cargo.lock index 10934c5..6510e80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1002,9 +1002,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "8e893f6bece5953520ddbb3f8f46f3ef36dd1fef4ee9b087c4b4a725fd5d10e4" dependencies = [ "base64", "bytes", @@ -1576,6 +1576,7 @@ dependencies = [ "anyhow", "clap", "fluent-templates", + "once_cell", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 775aaa1..8ae1124 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,11 @@ edition = "2024" [dependencies] clap = { version = "4.5", features = ["derive", "env"] } -reqwest = { version = "0.12", features = ["blocking", "json"] } +reqwest = { version = "0.12.27", features = ["blocking", "json"] } anyhow = "1.0" fluent-templates = "0.13.2" unic-langid = "0.9.6" sys-locale = "0.3.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.146" +once_cell = "1.19" diff --git a/formgen.txt b/formgen.txt deleted file mode 100644 index 0f5a3ac..0000000 --- a/formgen.txt +++ /dev/null @@ -1,124 +0,0 @@ -. -├── Cargo.toml -├── formgen.txt -├── .gitignore -└── src - ├── i18n - │   ├── loader.rs - │   ├── mod.rs - │   └── translate.rs - ├── locales - │   ├── en-US - │   │   └── main.ftl - │   └── pt-BR - │   └── main.ftl - └── main.rs - -6 directories, 9 files - - -=== CONTEÚDO DOS ARQUIVOS === - - -=== ./src/main.rs === - -use anyhow::{Ok, Result}; -use clap::Parser; -use reqwest::blocking::Client; -mod i18n; -use i18n::{init_locales, t}; - -#[derive(Debug, Parser)] -#[command(author, version, about)] -struct Args { - #[arg(short, long, env = "UPTIME_KUMA_URL")] - url: Option, - - #[arg(short, long, env = "UPTIME_KUMA_API_KEY")] - api_key: Option, -} - -fn main() -> Result<()> { - init_locales()?; - - let args = Args::parse(); - let url = args - .url - .as_deref() - .ok_or_else(|| anyhow::anyhow!("{}", t("missing_url")))?; - let api_key = args - .api_key - .as_deref() - .ok_or_else(|| anyhow::anyhow!("{}", t("missing_api_key")))?; - - let client = Client::new(); - - let response = client.get(url).basic_auth("", Some(api_key)).send()?; - - if response.status().is_success() { - let metrics = response.text()?; - println!("{}", t("success")); - println!("{}", t("metrics_preview")); - println!("{}", &metrics[..200.min(metrics.len())]); - } else { - println!("{}", response.status()); - println!("{}: {}", t("Response"), response.text()?); - } - Ok(()) -} - -=== ./src/i18n/translate.rs === - -use crate::i18n::loader::LOCALES; -use fluent_templates::Loader; -use std::str::FromStr; -use unic_langid::LanguageIdentifier; - -fn get_sys_locale() -> LanguageIdentifier { - let sys_lang = sys_locale::get_locale().unwrap_or_else(|| String::from("pt-BR")); - LanguageIdentifier::from_str(&sys_lang).expect("Invalid language") -} - -pub fn t(key: &str) -> String { - LOCALES.lookup(&get_sys_locale(), key) -} - -=== ./src/i18n/loader.rs === - -use anyhow::Ok; -use fluent_templates::static_loader; - -static_loader! { - pub static LOCALES = { - locales: "./src/locales", - fallback_language: "pt-BR", - customise: |bundle| bundle.set_use_isolating(false), - }; -} - -pub fn init_locales() -> anyhow::Result<()> { - Ok(()) -} - -=== ./src/i18n/mod.rs === - -pub mod loader; -pub mod translate; - -pub use loader::init_locales; -pub use translate::t; - -=== ./Cargo.toml === - -[package] -name = "uptime-kuma-dashboard" -version = "0.1.0" -edition = "2024" - -[dependencies] -clap = { version = "4.5", features = ["derive", "env"] } -reqwest = { version = "0.12", features = ["blocking", "json"] } -anyhow = "1.0" -fluent-templates = "0.13.2" -unic-langid = "0.9.6" -sys-locale = "0.3.2" diff --git a/src/heartbeat/model.rs b/src/heartbeat/model.rs index 4b3a8e4..728599d 100644 --- a/src/heartbeat/model.rs +++ b/src/heartbeat/model.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use crate::i18n::t; +use crate::i18n::{t, t_with_args}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct HeartbeatEntry { @@ -64,7 +64,13 @@ impl HeartbeatResponse { for (key, percentage) in &self.uptime_list_raw { let parts: Vec<&str> = key.split('_').collect(); if parts.len() != 2 { - return Err(anyhow::anyhow!("Formato invalido (colocar o t) {}", key)); + let t_args: &mut HashMap<&str, std::string::String> = + &mut HashMap::<&str, String>::new(); + t_args.insert("key", key.to_string()); + return Err(anyhow::anyhow!(t_with_args( + "invalid-uptime-key-format", + t_args + ))); } let monitor_id = parts[0] diff --git a/src/i18n/mod.rs b/src/i18n/mod.rs index b12fbd8..998db65 100644 --- a/src/i18n/mod.rs +++ b/src/i18n/mod.rs @@ -2,4 +2,4 @@ pub mod loader; pub mod translate; pub use loader::init_locales; -pub use translate::t; +pub use translate::{t, t_with_args}; diff --git a/src/i18n/translate.rs b/src/i18n/translate.rs index aed7b7c..2cae04a 100644 --- a/src/i18n/translate.rs +++ b/src/i18n/translate.rs @@ -1,13 +1,27 @@ use crate::i18n::loader::LOCALES; -use fluent_templates::Loader; -use std::str::FromStr; +use fluent_templates::{Loader, fluent_bundle::FluentValue}; +use once_cell::sync::Lazy; +use std::borrow::Cow; +use std::{collections::HashMap, str::FromStr}; use unic_langid::LanguageIdentifier; -fn get_sys_locale() -> LanguageIdentifier { +static SYSTEM_LOCALE: Lazy = Lazy::new(|| { let sys_lang = sys_locale::get_locale().unwrap_or_else(|| String::from("pt-BR")); LanguageIdentifier::from_str(&sys_lang).expect("Invalid language") -} +}); pub fn t(key: &str) -> String { - LOCALES.lookup(&get_sys_locale(), key) + LOCALES.lookup(&*SYSTEM_LOCALE, key) +} + +pub fn t_with_args(key: &str, args: &HashMap<&'static str, String>) -> String { + let mut map = HashMap::new(); + let args_map: &HashMap, FluentValue<'_>>; + args_map = { + for (key, value) in args { + map.insert(Cow::Borrowed(*key), FluentValue::from(value.clone())); + }; + &map + }; + LOCALES.lookup_with_args(&*&SYSTEM_LOCALE, key, args_map) } diff --git a/src/locales/en-US/main.ftl b/src/locales/en-US/main.ftl index d799d93..2dee732 100644 --- a/src/locales/en-US/main.ftl +++ b/src/locales/en-US/main.ftl @@ -5,3 +5,6 @@ metrics_preview = 📋 First 200 characters of metrics: Response = Response invalid-json-status-page = ❌ Error parssing status page JSON invalid-json-heartbeat = ❌ Error parssing heartbeat JSON +invalid-uptime-key-format = Invalid format for uptime key. Expected format "monitorID_period". Received key: {key} +invalid-monitor-id = Invalid monitor ID: {id} +invalid-period-hours = Invalid period in hours: {hours} diff --git a/src/locales/pt-BR/main.ftl b/src/locales/pt-BR/main.ftl index dff7127..518b1c0 100644 --- a/src/locales/pt-BR/main.ftl +++ b/src/locales/pt-BR/main.ftl @@ -5,3 +5,6 @@ missing_url = ❌ URL não fornecida. Use --url ou a variável de ambiente UPTIM Response = Resposta invalid-json-status-page = ❌ Falha ao parsear JSON do status page invalid-json-heartbeat = ❌ Falha ao parsear JSON do heartbeat +invalid-uptime-key-format = Formato inválido na chave de uptime. Chave esperada no formato "monitorID_periodo". Chave recebida: {key} +invalid-monitor-id = ID de monitor inválido: {id} +invalid-period-hours = Período em horas inválido: {hours} diff --git a/src/status_page/parser.rs b/src/status_page/parser.rs index 981b4df..37857d5 100644 --- a/src/status_page/parser.rs +++ b/src/status_page/parser.rs @@ -3,6 +3,7 @@ use crate::i18n::t; use anyhow::{Context, Ok, Result}; pub fn parse_response(json_text: &str) -> Result { - let mut response: StatusPageResponse = serde_json::from_str(json_text)?; //.with_context(|| t("invalid-json-status-page"))?; + let mut response: StatusPageResponse = + serde_json::from_str(json_text).with_context(|| t("invalid-json-status-page"))?; Ok(response) }