crates/gunnar-ui-web/src/front_certs.rs
//! **What the Certificates panel says**, on the host side of the wasm boundary (owner
//! 2026-10-25). The FRONT words every certificate (gunnar-front's `web::status`); this only
//! decides whether "Try now" is drawn pressable, and what it sends.
use serde::Deserialize;
/// The console's routes (`gunnar-ui-server::front_certs`).
pub const CERTS_API: &str = "/api/front/certs";
pub const TRY_API: &str = "/api/front/certs/try";
/// One certificate as the front says it.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct Cert {
pub host: String,
pub hosts: Vec<String>,
pub lines: Vec<String>,
pub try_now: bool,
pub alert: Option<String>,
}
/// `GET /api/front/certs`.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct Certs {
pub certificates: Vec<Cert>,
}
/// The answer of a try now.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct Tried {
pub words: String,
}
/// Pure: **"Try now"** — `(may be pressed, why not)`: an admin's, and only where the front
/// says it is taken (it refuses while a failed validation's wait runs).
pub fn button(c: &Cert, admin: bool) -> (bool, String) {
if !admin {
return (false, "an admin asks the front to try now".into());
}
if !c.try_now {
return (false, "the last attempt failed Let's Encrypt's validation; the front waits it out".into());
}
(true, String::new())
}
/// Pure: the body of a try now.
pub fn try_body(c: &Cert) -> serde_json::Value {
serde_json::json!({ "host": c.host })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_now_is_an_admins_and_only_where_the_front_allows_it() {
let c: Certs = serde_json::from_str(r#"{"certificates":[{"host":"gunnar.example","hosts":["gunnar.example"],"lines":["valid until: no valid certificate"],"try_now":true}]}"#).unwrap();
let one = &c.certificates[0];
assert_eq!(button(one, true), (true, String::new()));
assert!(!button(one, false).0 && button(one, false).1.contains("admin"));
let refused = Cert { try_now: false, ..one.clone() };
assert!(!button(&refused, true).0 && button(&refused, true).1.contains("validation"));
assert_eq!(try_body(one)["host"], "gunnar.example");
}
}