Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar-ui

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");
    }
}