diff --git a/src/build.rs b/src/build.rs index ebac904..f21d5bb 100644 --- a/src/build.rs +++ b/src/build.rs @@ -5,7 +5,7 @@ fn git_info() { if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { if output.status.success() { let git_hash = String::from_utf8_lossy(&output.stdout); - println!("cargo:rustc-env=GIT_HASH={}", git_hash); + println!("cargo:rustc-env=GIT_HASH={git_hash}"); println!("cargo:rustc-env=GIT_SHORT_HASH={}", &git_hash[..8]) } } @@ -16,7 +16,7 @@ fn git_info() { { if output.status.success() { let git_branch = String::from_utf8_lossy(&output.stdout); - println!("cargo:rustc-env=GIT_BRANCH={}", git_branch); + println!("cargo:rustc-env=GIT_BRANCH={git_branch}"); } } } @@ -32,11 +32,11 @@ fn version_info() -> Result<(), anyhow::Error> { let data: toml::Value = toml::from_str(&cargo_data)?; if let Some(version) = data["package"]["version"].as_str() { - println!("cargo:rustc-env=PKG_VERSION={}", version); + println!("cargo:rustc-env=PKG_VERSION={version}"); } if let Some(name) = data["package"]["name"].as_str() { - println!("cargo:rustc-env=PKG_NAME={}", name); + println!("cargo:rustc-env=PKG_NAME={name}"); } Ok(()) diff --git a/src/collector/stats.rs b/src/collector/stats.rs index c8d1812..91f235f 100644 --- a/src/collector/stats.rs +++ b/src/collector/stats.rs @@ -40,11 +40,11 @@ impl std::fmt::Display for Counter { let labels = self .labels .iter() - .map(|(k, v)| format!("{}: {}", k, v)) + .map(|(k, v)| format!("{k}: {v}")) .collect::>() .join(", "); - write!(f, "{} - {}", labels, self.value) + write!(f, "{labels} - {}", self.value) } } @@ -59,11 +59,11 @@ impl std::fmt::Display for Gauge { let labels = self .labels .iter() - .map(|(k, v)| format!("{}: {}", k, v)) + .map(|(k, v)| format!("{k}: {v}")) .collect::>() .join(", "); - write!(f, "{} - {}", labels, self.value) + write!(f, "{labels} - {}", self.value) } } @@ -78,7 +78,7 @@ impl std::fmt::Display for Histogram { let labels = self .labels .iter() - .map(|(k, v)| format!("{}: {}", k, v)) + .map(|(k, v)| format!("{k}: {v}")) .collect::>() .join(", "); @@ -87,15 +87,15 @@ impl std::fmt::Display for Histogram { .iter() .map(|(k, v)| { if let Some(v) = v { - format!("{}: {:.6}", k, v) + format!("{k}: {v:.6}") } else { - format!("{}: None,", k) + format!("{k}: None,") } }) .collect::>() .join(", "); - write!(f, "{} - {}", labels, value) + write!(f, "{labels} - {value}") } } @@ -172,18 +172,18 @@ impl Snapshot { continue; } - println!("\t{}", key); + println!("\t{key}"); for counter in counters { - println!("\t\t{}", counter); + println!("\t\t{counter}"); } } for (key, counters) in merging { - println!("\t{}", key); + println!("\t{key}"); for (_, counter) in counters { if let Some(counter) = counter.merge() { - println!("\t\t{}", counter); + println!("\t\t{counter}"); } } } @@ -192,10 +192,10 @@ impl Snapshot { if !self.gauges.is_empty() { println!("Gauges"); for (key, gauges) in self.gauges { - println!("\t{}", key); + println!("\t{key}"); for gauge in gauges { - println!("\t\t{}", gauge); + println!("\t\t{gauge}"); } } } @@ -203,10 +203,10 @@ impl Snapshot { if !self.histograms.is_empty() { println!("Histograms"); for (key, histograms) in self.histograms { - println!("\t{}", key); + println!("\t{key}"); for histogram in histograms { - println!("\t\t{}", histogram); + println!("\t\t{histogram}"); } } } diff --git a/src/config.rs b/src/config.rs index 78caaf5..db21f9d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -170,7 +170,7 @@ impl Config { let config: ParsedConfig = config.try_deserialize()?; let scheme = if config.https { "https" } else { "http" }; - let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute(); + let base_uri = iri!(format!("{scheme}://{}", config.hostname)).into_absolute(); let tls = match (config.tls_key, config.tls_cert) { (Some(key), Some(cert)) => Some(TlsConfig { key, cert }), @@ -207,8 +207,8 @@ impl Config { let source_url = match Self::git_hash() { Some(hash) => format!( - "{}{}{}", - config.source_repo, config.repository_commit_base, hash + "{}{}{hash}", + config.source_repo, config.repository_commit_base ) .parse() .expect("constructed source URL is valid"), @@ -332,7 +332,7 @@ impl Config { match AdminConfig::build(api_token) { Ok(conf) => Some(actix_web::web::Data::new(conf)), Err(e) => { - tracing::error!("Error creating admin config: {}", e); + tracing::error!("Error creating admin config: {e}"); None } } @@ -371,7 +371,7 @@ impl Config { pub(crate) fn software_version() -> String { if let Some(git) = Self::git_version() { - return format!("v{}-{}", Self::version(), git); + return format!("v{}-{git}", Self::version()); } format!("v{}", Self::version()) @@ -381,7 +381,7 @@ impl Config { let branch = Self::git_branch()?; let hash = Self::git_short_hash()?; - Some(format!("{}-{}", branch, hash)) + Some(format!("{branch}-{hash}")) } fn name() -> &'static str { @@ -463,7 +463,7 @@ impl Config { resolved } UrlKind::Media(uuid) => FixedBaseResolver::new(self.base_uri.as_ref()) - .resolve(IriRelativeStr::new(&format!("media/{}", uuid))?.as_ref()) + .resolve(IriRelativeStr::new(&format!("media/{uuid}"))?.as_ref()) .try_to_dedicated_string()?, UrlKind::NodeInfo => FixedBaseResolver::new(self.base_uri.as_ref()) .resolve(IriRelativeStr::new("nodeinfo/2.0.json")?.as_ref()) diff --git a/src/data/node.rs b/src/data/node.rs index 4ba6eb5..ac89cdb 100644 --- a/src/data/node.rs +++ b/src/data/node.rs @@ -182,7 +182,7 @@ impl Node { let authority = url.authority_str().ok_or(ErrorKind::MissingDomain)?; let scheme = url.scheme_str(); - let base = iri!(format!("{}://{}", scheme, authority)); + let base = iri!(format!("{scheme}://{authority}")); Ok(Node { base, diff --git a/src/db.rs b/src/db.rs index 6d3064f..6713cba 100644 --- a/src/db.rs +++ b/src/db.rs @@ -10,7 +10,10 @@ use rsa::{ use sled::{Batch, Tree}; use std::{ collections::{BTreeMap, HashMap}, - sync::Arc, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, time::SystemTime, }; use time::OffsetDateTime; @@ -22,6 +25,8 @@ pub(crate) struct Db { } struct Inner { + healthz: Tree, + healthz_counter: Arc, actor_id_actor: Tree, public_key_id_actor_id: Tree, connected_actor_ids: Tree, @@ -242,6 +247,8 @@ impl Db { fn build_inner(restricted_mode: bool, db: sled::Db) -> Result { Ok(Db { inner: Arc::new(Inner { + healthz: db.open_tree("healthz")?, + healthz_counter: Arc::new(AtomicU64::new(0)), actor_id_actor: db.open_tree("actor-id-actor")?, public_key_id_actor_id: db.open_tree("public-key-id-actor-id")?, connected_actor_ids: db.open_tree("connected-actor-ids")?, @@ -273,6 +280,21 @@ impl Db { Ok(t) } + pub(crate) async fn check_health(&self) -> Result<(), Error> { + let next = self.inner.healthz_counter.fetch_add(1, Ordering::Relaxed); + self.unblock(move |inner| { + inner + .healthz + .insert("healthz", &next.to_be_bytes()[..]) + .map_err(Error::from) + }) + .await?; + self.inner.healthz.flush_async().await?; + self.unblock(move |inner| inner.healthz.get("healthz").map_err(Error::from)) + .await?; + Ok(()) + } + pub(crate) async fn mark_last_seen( &self, nodes: HashMap, @@ -468,7 +490,7 @@ impl Db { pub(crate) async fn is_connected(&self, base_id: IriString) -> Result { let scheme = base_id.scheme_str(); let authority = base_id.authority_str().ok_or(ErrorKind::MissingDomain)?; - let prefix = format!("{}://{}", scheme, authority); + let prefix = format!("{scheme}://{authority}"); self.unblock(move |inner| { let connected = inner @@ -528,7 +550,7 @@ impl Db { } pub(crate) async fn remove_connection(&self, actor_id: IriString) -> Result<(), Error> { - tracing::debug!("Removing Connection: {}", actor_id); + tracing::debug!("Removing Connection: {actor_id}"); self.unblock(move |inner| { inner .connected_actor_ids @@ -540,7 +562,7 @@ impl Db { } pub(crate) async fn add_connection(&self, actor_id: IriString) -> Result<(), Error> { - tracing::debug!("Adding Connection: {}", actor_id); + tracing::debug!("Adding Connection: {actor_id}"); self.unblock(move |inner| { inner .connected_actor_ids diff --git a/src/jobs/instance.rs b/src/jobs/instance.rs index 2f1e5c7..650c8ce 100644 --- a/src/jobs/instance.rs +++ b/src/jobs/instance.rs @@ -45,7 +45,7 @@ impl QueryInstance { .authority_str() .ok_or(ErrorKind::MissingDomain)?; let scheme = self.actor_id.scheme_str(); - let instance_uri = iri!(format!("{}://{}/api/v1/instance", scheme, authority)); + let instance_uri = iri!(format!("{scheme}://{authority}/api/v1/instance")); let instance = match state .requests diff --git a/src/jobs/nodeinfo.rs b/src/jobs/nodeinfo.rs index fe86ad8..271b5a1 100644 --- a/src/jobs/nodeinfo.rs +++ b/src/jobs/nodeinfo.rs @@ -39,7 +39,7 @@ impl QueryNodeinfo { .authority_str() .ok_or(ErrorKind::MissingDomain)?; let scheme = self.actor_id.scheme_str(); - let well_known_uri = iri!(format!("{}://{}/.well-known/nodeinfo", scheme, authority)); + let well_known_uri = iri!(format!("{scheme}://{authority}/.well-known/nodeinfo")); let well_known = match state .requests @@ -168,7 +168,7 @@ impl<'de> serde::de::Visitor<'de> for SupportedVersionVisitor { type Value = SupportedVersion; fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "a string starting with '{}'", SUPPORTED_VERSIONS) + write!(f, "a string starting with '{SUPPORTED_VERSIONS}'") } fn visit_str(self, s: &str) -> Result @@ -187,7 +187,7 @@ impl<'de> serde::de::Visitor<'de> for SupportedNodeinfoVisitor { type Value = SupportedNodeinfo; fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "a string starting with '{}'", SUPPORTED_NODEINFO) + write!(f, "a string starting with '{SUPPORTED_NODEINFO}'") } fn visit_str(self, s: &str) -> Result diff --git a/src/main.rs b/src/main.rs index 39bba1d..22764a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ use self::{ db::Db, jobs::create_workers, middleware::{DebugPayload, MyVerify, RelayResolver, Timings}, - routes::{actor, inbox, index, nodeinfo, nodeinfo_meta, statics}, + routes::{actor, healthz, inbox, index, nodeinfo, nodeinfo_meta, statics}, }; fn init_subscriber( @@ -273,6 +273,7 @@ async fn do_server_main( app.wrap(Compress::default()) .wrap(TracingLogger::default()) .wrap(Timings) + .route("/healthz", web::get().to(healthz)) .service(web::resource("/").route(web::get().to(index))) .service(web::resource("/media/{path}").route(web::get().to(routes::media))) .service( diff --git a/src/middleware/verifier.rs b/src/middleware/verifier.rs index e9aa321..2ae9779 100644 --- a/src/middleware/verifier.rs +++ b/src/middleware/verifier.rs @@ -75,7 +75,7 @@ impl MyVerify { Ok(res) => res.actor_id().ok_or(ErrorKind::MissingId), Err(e) => { if e.is_gone() { - tracing::warn!("Actor gone: {}", public_key_id); + tracing::warn!("Actor gone: {public_key_id}"); return Ok(false); } else { return Err(e); @@ -178,13 +178,13 @@ mod tests { #[test] fn handles_masto_keys() { - println!("{}", ASONIX_DOG_KEY); + println!("{ASONIX_DOG_KEY}"); let _ = RsaPublicKey::from_public_key_pem(ASONIX_DOG_KEY.trim()).unwrap(); } #[test] fn handles_pleromo_keys() { - println!("{}", KARJALAZET_KEY); + println!("{KARJALAZET_KEY}"); let _ = RsaPublicKey::from_public_key_pem(KARJALAZET_KEY.trim()).unwrap(); } diff --git a/src/requests.rs b/src/requests.rs index d948a22..18b9840 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -61,7 +61,7 @@ impl Breakers { if let Some(mut breaker) = self.inner.get_mut(authority) { breaker.fail(); if !breaker.should_try() { - tracing::warn!("Failed breaker for {}", authority); + tracing::warn!("Failed breaker for {authority}"); } false } else { @@ -235,7 +235,7 @@ impl Requests { if let Ok(bytes) = res.body().await { if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { if !s.is_empty() { - tracing::warn!("Response from {}, {}", parsed_url, s); + tracing::warn!("Response from {parsed_url}, {s}"); } } } diff --git a/src/routes.rs b/src/routes.rs index d80a0a1..9afd38b 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,4 +1,5 @@ mod actor; +mod healthz; mod inbox; mod index; mod media; @@ -7,6 +8,7 @@ mod statics; pub(crate) use self::{ actor::route as actor, + healthz::route as healthz, inbox::route as inbox, index::route as index, media::route as media, diff --git a/src/routes/healthz.rs b/src/routes/healthz.rs new file mode 100644 index 0000000..f297ba5 --- /dev/null +++ b/src/routes/healthz.rs @@ -0,0 +1,7 @@ +use crate::{data::State, error::Error}; +use actix_web::{web, HttpResponse}; + +pub(crate) async fn route(state: web::Data) -> Result { + state.db.check_health().await?; + Ok(HttpResponse::Ok().finish()) +} diff --git a/src/telegram.rs b/src/telegram.rs index 270e1af..f3cce43 100644 --- a/src/telegram.rs +++ b/src/telegram.rs @@ -89,19 +89,19 @@ async fn answer(bot: Bot, msg: Message, cmd: Command, db: Db) -> ResponseResult< .await?; } Command::Block { domain } if db.add_blocks(vec![domain.clone()]).await.is_ok() => { - bot.send_message(msg.chat.id, format!("{} has been blocked", domain)) + bot.send_message(msg.chat.id, format!("{domain} has been blocked")) .await?; } Command::Unblock { domain } if db.remove_blocks(vec![domain.clone()]).await.is_ok() => { - bot.send_message(msg.chat.id, format!("{} has been unblocked", domain)) + bot.send_message(msg.chat.id, format!("{domain} has been unblocked")) .await?; } Command::Allow { domain } if db.add_allows(vec![domain.clone()]).await.is_ok() => { - bot.send_message(msg.chat.id, format!("{} has been allowed", domain)) + bot.send_message(msg.chat.id, format!("{domain} has been allowed")) .await?; } Command::Disallow { domain } if db.remove_allows(vec![domain.clone()]).await.is_ok() => { - bot.send_message(msg.chat.id, format!("{} has been disallowed", domain)) + bot.send_message(msg.chat.id, format!("{domain} has been disallowed")) .await?; } Command::ListAllowed => {