From 067a73d9820dd633e6af6d26b81778f1cf413899 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Fri, 9 Oct 2026 05:43:07 +0200 Subject: [PATCH] --wip-- [skip ci] --- Cargo.lock | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 95 +++++++++++++++++++++++++++++++++------------ 3 files changed, 180 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94224f9..248ee26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,70 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "axum-macros", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "base64" version = "0.23.1" @@ -293,6 +357,7 @@ name = "crete" version = "0.1.3" dependencies = [ "async-walkdir", + "axum", "byteorder", "clap", "clap_complete", @@ -860,6 +925,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.11.1" @@ -873,6 +944,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1211,6 +1283,12 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.8.3" @@ -1232,6 +1310,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c8c750f1a07ea702bbd212bd999fceece9b3d1508b17023b3e174583124b" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minijinja" version = "2.24.0" @@ -1790,6 +1874,29 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -2031,6 +2138,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -2069,6 +2177,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ + "log", "pin-project-lite", "tracing-core", ] diff --git a/Cargo.toml b/Cargo.toml index 6e7eb7c..f300252 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ serde_yaml = "0.9.34" thiserror = "2.0.21" tokio = { version = "1.52.1", features = ["full"] } ipxe = { path = "./ipxe" } +axum = { version = "0.8.9", features = ["macros"] } [lib] test = false diff --git a/src/main.rs b/src/main.rs index 09fccfc..c27cf0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,12 @@ mod cli; +use std::path::PathBuf; use std::pin::pin; +use axum::extract::Request; +use axum::response::IntoResponse; +use axum::routing::get; +use axum::{Router, debug_handler}; use clap::{CommandFactory, Parser}; use clap_complete::{Shell, generate as generate_complete}; use crete::cluster::Cluster; @@ -14,6 +19,7 @@ use futures::future::join_all; use ipxe::IPXE_DIR; use minijinja::context; use regex::Regex; +use reqwest::{StatusCode, header}; use thiserror::Error; use crate::cli::{Cli, Commands, GlobalOpts}; @@ -93,6 +99,63 @@ async fn generate(opts: &GlobalOpts) -> Result<(), Error> { Ok(()) } +#[debug_handler] +async fn handler(req: Request) -> impl IntoResponse { + let Some(path) = req.uri().path().strip_prefix("/") else { + return StatusCode::BAD_REQUEST.into_response(); + }; + + let Some((file, content_type)) = get_file(path).await else { + return StatusCode::NOT_FOUND.into_response(); + }; + + let path = PathBuf::from(path); + let filename = path.file_name().unwrap(); + let filename = filename.to_str().unwrap(); + + let content_type = [(header::CONTENT_TYPE, content_type)]; + let disposition = [( + header::CONTENT_DISPOSITION, + format!("inline; filename=\"{filename}\""), + )]; + + (content_type, disposition, file).into_response() +} + +// TODO: Make this a proper async function +fn get_file(filename: impl Into) -> impl Future, &'static str)>> { + // TODO: Make this a static that is initialized once + let re = + Regex::new(r"^talos/(?.+)/(?.+)\.ya?ml$").expect("regex should be valid"); + + let filename: String = filename.into(); + let re = re.clone(); + + async move { + const IPXE_PATTERN: &str = "ipxe/"; + if let Some(filename) = filename.strip_prefix(IPXE_PATTERN) { + Some(( + IPXE_DIR.get_file(filename)?.contents().to_owned(), + "application/octet-stream", + )) + } else if let Some(caps) = re.captures(&filename) { + let cluster = &caps["cluster"]; + let node = &caps["node"]; + + let clusters = Cluster::get_all().await; + let cluster = clusters.get(cluster)?; + + let node = cluster.node(node)?; + + let config = node.config(cluster).await; + + Some((config.into(), "application/yaml; charset=utf-8")) + } else { + None + } + } +} + #[tokio::main] async fn main() -> Result<(), Error> { let cli = Cli::parse(); @@ -108,35 +171,17 @@ async fn main() -> Result<(), Error> { &mut std::io::stdout(), ), Commands::Serve => { - let re = Regex::new(r"talos/(?.+)/(?.+)\.ya?ml") - .expect("regex should be valid"); + tokio::spawn(async { + let app = Router::new().fallback_service(get(handler)); + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + axum::serve(listener, app).await.unwrap(); + }); tftp::serve(move |filename| { let filename = filename.to_owned(); - let re = re.clone(); - - async move { - const IPXE_PATTERN: &str = "ipxe/"; - if let Some(filename) = filename.strip_prefix(IPXE_PATTERN) { - Some(IPXE_DIR.get_file(filename)?.contents().to_owned()) - } else if let Some(caps) = re.captures(&filename) { - let cluster = &caps["cluster"]; - let node = &caps["node"]; - - let clusters = Cluster::get_all().await; - let cluster = clusters.get(cluster)?; - - let node = cluster.node(node)?; - - let config = node.config(cluster).await; - - Some(config.into()) - } else { - None - } - } + async move { get_file(filename).await.map(|(file, _)| file) } }) - .await? + .await?; } };