//! Normal-operation status server.
//!
//! Serves one HTML page over the station IP plus any number of JSON endpoints.
//! The crate supplies the style shell and the plumbing; *what* a device
//! reports is entirely the consumer's (`nfc-522` exposes recent card reads,
//! `lambent-flex` its UDP port map).
//!
//! Read-only by design — it accepts no config. Reconfiguration goes through
//! [`crate::portal`], which only runs in provisioning mode.
use anyhow::Result;
use esp_idf_svc::http::server::{Configuration, EspHttpServer};
use esp_idf_svc::http::Method;
use esp_idf_svc::io::Write as _;
use crate::assets::{Asset, H_JSON};
use crate::http::{serve_asset, serve_shell};
/// A status server under construction.
///
/// The built [`EspHttpServer`] **must be kept alive** — dropping it
/// unregisters every handler.
pub struct StatusServer {
server: EspHttpServer<'static>,
}
impl StatusServer {
/// Start a server serving `page` at `/`, plus the shared CSS and fonts.
pub fn new(page: Asset) -> Result<Self> {
let mut server = EspHttpServer::new(&Configuration::default())?;
serve_asset(&mut server, "/", page)?;
serve_shell(&mut server)?;
Ok(Self { server })
}
/// Start a server with the shared CSS and fonts but **no page at `/`**.
///
/// For firmware that wants to keep `/` for something else (an existing
/// JSON API, say) and serve its status page elsewhere.
pub fn bare() -> Result<Self> {
let mut server = EspHttpServer::new(&Configuration::default())?;
serve_shell(&mut server)?;
Ok(Self { server })
}
/// Register a JSON endpoint.
///
/// `body` is called per request and returns the encoded response. An error
/// becomes a `500` rather than killing the handler, so a transient
/// serialization failure cannot take the status page down.
pub fn json<F>(mut self, uri: &str, body: F) -> Result<Self>
where
F: Fn() -> Result<Vec<u8>> + Send + 'static,
{
self.server.fn_handler(uri, Method::Get, move |req| {
match body() {
Ok(bytes) => {
req.into_response(200, Some("OK"), H_JSON)?
.write_all(&bytes)?;
}
Err(e) => {
log::warn!("status json handler failed: {e:#}");
req.into_response(500, Some("Internal Server Error"), &[])?;
}
}
Ok::<(), anyhow::Error>(())
})?;
Ok(self)
}
/// Register an extra static asset (an icon, a second page, …).
pub fn asset(mut self, uri: &str, asset: Asset) -> Result<Self> {
serve_asset(&mut self.server, uri, asset)?;
Ok(self)
}
/// Escape hatch for routes this builder does not cover.
pub fn server_mut(&mut self) -> &mut EspHttpServer<'static> {
&mut self.server
}
/// Finish, yielding the live server to be held for the program's lifetime.
pub fn build(self) -> EspHttpServer<'static> {
self.server
}
}
neiam
/neiam-portal
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
3.0 KiB · text
History
65cab78