src/pages/front_certs.rs
//! Admin → **Certificates** (owner 2026-10-25): this front's certificates as the front says
//! them, and "try now" where it allows it. Every decision is in [`crate::front_certs`].
use leptos::prelude::*;
use crate::components::feedback::{ErrorText, Loading};
use crate::front_certs::{button, read, try_body, Cert, CERTS_PATH, TRY_PATH};
/// The Certificates pane.
#[component]
pub fn CertsPane(generation: RwSignal<u32>) -> impl IntoView {
let certs = LocalResource::new(move || {
let _ = generation.get();
async move {
let (status, body) = crate::session::door(CERTS_PATH, None).await?;
read(status, &body)
}
});
let said = RwSignal::new(None::<String>);
let press = move |c: Cert| {
leptos::task::spawn_local(async move {
let words = match crate::session::door(TRY_PATH, Some(&try_body(&c))).await {
Ok((200, body)) => serde_json::from_str::<serde_json::Value>(&body).ok().and_then(|v| v["words"].as_str().map(str::to_string)).unwrap_or(body),
Ok((_, body)) => body,
Err(e) => e,
};
said.set(Some(words));
generation.update(|g| *g += 1);
});
};
view! {
<div>
{move || said.get().map(|w| view! { <p class="count" role="status">{w}</p> })}
{move || match certs.get() {
None => view! { <Loading what="the certificates"/> }.into_any(),
Some(Err(e)) => view! { <ErrorText context="certificates" message=e/> }.into_any(),
Some(Ok(c)) if c.certificates.is_empty() => view! { <p class="count">"No certificate is known to this front yet."</p> }.into_any(),
Some(Ok(c)) => view! {
<div>
{c.certificates.into_iter().map(|cert| {
let (may, why) = button(&cert);
let shown = cert.clone();
view! {
<section class="card">
<h3 class="mono">{cert.host.clone()}</h3>
{cert.alert.clone().map(|a| view! { <p class="bad" role="alert">{a}</p> })}
<pre class="mono">{cert.lines.join("\n")}</pre>
<button title=why disabled=!may on:click=move |_| press(shown.clone())>"Try now"</button>
</section>
}
}).collect_view()}
</div>
}.into_any(),
}}
</div>
}
}