//! Defines the HTTP-based UI. These endpoints generally return HTML and SVG. use std::{sync::Arc, time::Duration}; use futures::{future, Future}; use hyper::{header, service::Service, Body, Method, Request, Response, StatusCode}; use ritz::html; use crate::{ imfs::ImfsFetcher, serve_session::ServeSession, web::{ interface::{ErrorResponse, SERVER_VERSION}, util::json, }, }; static LOGO: &[u8] = include_bytes!("../../assets/logo-512.png"); static ICON: &[u8] = include_bytes!("../../assets/icon-32.png"); static HOME_CSS: &str = include_str!("../../assets/index.css"); pub struct UiService { serve_session: Arc>, } impl Service for UiService { type ReqBody = Body; type ResBody = Body; type Error = hyper::Error; type Future = Box, Error = Self::Error> + Send>; fn call(&mut self, request: Request) -> Self::Future { let response = match (request.method(), request.uri().path()) { (&Method::GET, "/") => self.handle_home(), (&Method::GET, "/logo.png") => self.handle_logo(), (&Method::GET, "/icon.png") => self.handle_icon(), (&Method::GET, "/visualize/rbx") => self.handle_visualize_rbx(), (&Method::GET, "/visualize/imfs") => self.handle_visualize_imfs(), (_method, path) => { return json( ErrorResponse::not_found(format!("Route not found: {}", path)), StatusCode::NOT_FOUND, ) } }; Box::new(future::ok(response)) } } impl UiService { pub fn new(serve_session: Arc>) -> Self { UiService { serve_session } } fn handle_logo(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "image/png") .body(Body::from(LOGO)) .unwrap() } fn handle_icon(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "image/png") .body(Body::from(ICON)) .unwrap() } fn handle_home(&self) -> Response { let project_name = self.serve_session.project_name().unwrap_or(""); let uptime = { let elapsed = self.serve_session.start_time().elapsed(); // Round off all of our sub-second precision to make timestamps // nicer. let just_nanos = Duration::from_nanos(elapsed.subsec_nanos() as u64); let elapsed = elapsed - just_nanos; humantime::format_duration(elapsed).to_string() }; let page = html! { "Rojo Live Server"
"Server Version: " { SERVER_VERSION } "Project: " { project_name } "Server Uptime: " { uptime.to_string() }
}; Response::builder() .header(header::CONTENT_TYPE, "text/html") .body(Body::from(format!("{}", page))) .unwrap() } fn handle_visualize_rbx(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "text/plain") .body(Body::from("TODO: /visualize/rbx")) .unwrap() } fn handle_visualize_imfs(&self) -> Response { Response::builder() .header(header::CONTENT_TYPE, "text/plain") .body(Body::from("TODO: /visualize/imfs")) .unwrap() } }