Serve new UI from axum server

This commit is contained in:
Joscha 2025-03-03 19:36:32 +01:00
parent 5e60dd2e30
commit 38994a86ae
6 changed files with 51 additions and 69 deletions

View file

@ -1,8 +1,10 @@
pub const UNIFONT: &[u8] = include_bytes!("../data/unifont-15.1.05.otf"); pub const UNIFONT: &[u8] = include_bytes!("../data/unifont-15.1.05.otf");
pub const UNIFONT_JP: &[u8] = include_bytes!("../data/unifont_jp-15.1.05.otf"); pub const UNIFONT_JP: &[u8] = include_bytes!("../data/unifont_jp-15.1.05.otf");
pub const UNIFONT_UPPER: &[u8] = include_bytes!("../data/unifont_upper-15.1.05.otf"); pub const UNIFONT_UPPER: &[u8] = include_bytes!("../data/unifont_upper-15.1.05.otf");
pub const UNIFONT_NAME: &str = "Unifont";
pub const UNIFONT_SIZE: f32 = 16.0; pub const UNIFONT_NAME: &str = "unifont-15.1.05.otf";
pub const UNIFONT_JP_NAME: &str = "unifont_jp-15.1.05.otf";
pub const UNIFONT_UPPER_NAME: &str = "unifont_upper-15.1.05.otf";
pub const EGG_BAD_COVERS: &[&[u8]] = &[include_bytes!("../data/egg_bad/cover_00.png")]; pub const EGG_BAD_COVERS: &[&[u8]] = &[include_bytes!("../data/egg_bad/cover_00.png")];
pub const EGG_BAD_PATTERNS: &[&[u8]] = &[ pub const EGG_BAD_PATTERNS: &[&[u8]] = &[

View file

@ -0,0 +1 @@
../showbits-thermal-printer-ui/dist

View file

@ -12,8 +12,6 @@ use tokio::{net::TcpListener, sync::mpsc};
use crate::{documents, drawer::Command}; use crate::{documents, drawer::Command};
use self::r#static::get_static_file;
#[derive(Clone)] #[derive(Clone)]
pub struct Server { pub struct Server {
tx: mpsc::Sender<Command>, tx: mpsc::Sender<Command>,
@ -27,6 +25,12 @@ impl Server {
pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()> { pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()> {
let app = Router::new() let app = Router::new()
// Files
.route("/", get(r#static::get_index))
.route("/assets/{*path}", get(r#static::get_asset))
.route("/fonts/{*path}", get(r#static::get_font))
.route("/photo.html", get(r#static::get_photo))
// API
.route("/api/calendar", post(documents::calendar::post)) .route("/api/calendar", post(documents::calendar::post))
.route("/api/cells", post(documents::cells::post)) .route("/api/cells", post(documents::cells::post))
.route("/api/chat", post(documents::chat::post)) .route("/api/chat", post(documents::chat::post))
@ -34,7 +38,7 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
.route("/api/image", post(documents::image::post)) .route("/api/image", post(documents::image::post))
.route("/api/text", post(documents::text::post)) .route("/api/text", post(documents::text::post))
.route("/api/tictactoe", post(documents::tictactoe::post)) .route("/api/tictactoe", post(documents::tictactoe::post))
.fallback(get(get_static_file)) // Rest
.layer(DefaultBodyLimit::max(32 * 1024 * 1024)) // 32 MiB .layer(DefaultBodyLimit::max(32 * 1024 * 1024)) // 32 MiB
.with_state(Server { tx }); .with_state(Server { tx });

View file

@ -1,62 +1,53 @@
use axum::{ use axum::{
http::{StatusCode, Uri, header}, extract::Path,
http::{StatusCode, header},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use showbits_assets::{
UNIFONT, UNIFONT_JP, UNIFONT_JP_NAME, UNIFONT_NAME, UNIFONT_UPPER, UNIFONT_UPPER_NAME,
};
use super::statuscode::status_code; use super::statuscode::status_code;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "static"] #[folder = "dist/assets"]
struct StaticFiles; struct Assets;
struct StaticFile(String); pub async fn get_asset(Path(path): Path<String>) -> impl IntoResponse {
match Assets::get(&path) {
fn look_up_path(path: &str) -> Option<Response> { None => status_code(StatusCode::NOT_FOUND),
let path = path.trim_start_matches('/'); Some(content) => {
let file = StaticFiles::get(path)?; let mime = mime_guess::from_path(&path).first_or_octet_stream();
let mime = mime_guess::from_path(path).first_or_octet_stream(); ([(header::CONTENT_TYPE, mime.as_ref())], content.data).into_response()
let response = ([(header::CONTENT_TYPE, mime.as_ref())], file.data).into_response(); }
Some(response) }
} }
impl IntoResponse for StaticFile { pub async fn get_font(Path(path): Path<String>) -> Response {
fn into_response(self) -> Response { let font = if path == UNIFONT_NAME {
let mut path = self.0; UNIFONT
if path.is_empty() { } else if path == UNIFONT_JP_NAME {
path.push('/') UNIFONT_JP
} else if path == UNIFONT_UPPER_NAME {
UNIFONT_UPPER
} else {
return status_code(StatusCode::NOT_FOUND);
}; };
if path.ends_with(".html") { ([(header::CONTENT_TYPE, "font/otf")], font).into_response()
// A file `/foo/bar.html` should not be accessible directly, only
// indirectly at `/foo/bar`.
return status_code(StatusCode::NOT_FOUND);
} }
if path.ends_with("/index") { pub async fn get_index() -> impl IntoResponse {
// A file `/foo/index.html` should not be accessible directly, only (
// indirectly at `/foo/`. [(header::CONTENT_TYPE, "text/html; charset=utf-8")],
return status_code(StatusCode::NOT_FOUND); include_str!("../../dist/index.html"),
)
} }
if path.ends_with('/') { pub async fn get_photo() -> impl IntoResponse {
path.push_str("index"); (
} [(header::CONTENT_TYPE, "text/html; charset=utf-8")],
include_str!("../../dist/photo.html"),
if let Some(response) = look_up_path(&path) { )
return response;
}
path.push_str(".html");
if let Some(response) = look_up_path(&path) {
return response;
}
status_code(StatusCode::NOT_FOUND)
}
}
pub async fn get_static_file(uri: Uri) -> impl IntoResponse {
StaticFile(uri.path().to_string())
} }

View file

@ -1,16 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>TP: Index</title>
</head>
<body>
<h1>Thermal Printer Control</h1>
<ul>
<li><a href="image">Upload an image</a></li>
<li><a href="photo">Take a photo</a></li>
<li><a href="egg">Osterei</a></li>
</ul>
</body>
</html>