diff --git a/Cargo.lock b/Cargo.lock index c4895fc..9c01c8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,7 +188,10 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", + "time", + "wasm-bindgen", "winapi", ] @@ -200,7 +203,7 @@ checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552" dependencies = [ "chrono", "chrono-tz-build", - "phf", + "phf 0.10.1", ] [[package]] @@ -210,7 +213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069" dependencies = [ "parse-zoneinfo", - "phf", + "phf 0.10.1", "phf_codegen", ] @@ -461,7 +464,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -619,6 +622,15 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +[[package]] +name = "isolang" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f80f221db1bc708b71128757b9396727c04de86968081e18e89b0575e03be071" +dependencies = [ + "phf 0.11.1", +] + [[package]] name = "itoa" version = "1.0.6" @@ -736,7 +748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -869,7 +881,16 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_shared", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +dependencies = [ + "phf_shared 0.11.1", ] [[package]] @@ -879,7 +900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.10.0", ] [[package]] @@ -888,7 +909,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ - "phf_shared", + "phf_shared 0.10.0", "rand", ] @@ -902,6 +923,15 @@ dependencies = [ "uncased", ] +[[package]] +name = "phf_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -1365,6 +1395,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time-banner" version = "0.1.0" @@ -1379,11 +1420,22 @@ dependencies = [ "serde", "serde_json", "tera", + "timeago", "tokio", "tracing", "tracing-subscriber", ] +[[package]] +name = "timeago" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5082dc942361cdfb74eab98bf995762d6015e5bb3a20bf7c5c71213778b4fcb4" +dependencies = [ + "chrono", + "isolang", +] + [[package]] name = "tiny-skia" version = "0.10.0" @@ -1769,6 +1821,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 7bb8e5a..6899ed0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ dotenvy = "0.15.7" envy = "0.4.2" tera = "1.19.0" lazy_static = "1.4.0" +timeago = "0.4.1" diff --git a/src/main.rs b/src/main.rs index 25809f4..f8fc11b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,18 @@ mod config; use std::net::SocketAddr; +use std::time::SystemTime; use axum::{http::StatusCode, response::IntoResponse, Router, routing::{get}}; use axum::body::{Full}; -use axum::extract::ConnectInfo; +use axum::extract::{ConnectInfo, Path}; use axum::http::{header, HeaderMap}; use axum::response::Response; use dotenvy::dotenv; use lazy_static::lazy_static; use config::Configuration; use tera::{Tera, Context}; +use timeago::Formatter; mod svg; @@ -46,7 +48,7 @@ async fn main() { .with_max_level(config.log_level()) .init(); - let app = Router::new().route("/", get(root_handler)); + let app = Router::new().route("/:path", get(root_handler)); let addr = SocketAddr::from((config.socket_addr(), config.port)); axum::Server::bind(&addr) .serve(app.into_make_service_with_connect_info::()) @@ -54,21 +56,51 @@ async fn main() { .unwrap(); } -// basic handler that responds with a static string -async fn root_handler(connect_info: ConnectInfo, headers: HeaderMap) -> impl IntoResponse { - let renderer = svg::Renderer::new(); +fn convert_epoch(epoch: u64) -> SystemTime { + SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(epoch) +} - let mut context = Context::new(); +fn parse_path(path: String) -> Result<(SystemTime, String), String> { + if path.contains(".") { + let split_path = path.split_once(".").unwrap(); + let epoch_int = split_path.0.parse::(); + if epoch_int.is_err() { + return Err("Epoch is not a valid integer.".to_string()); + } - let mut ip_repr: Option = headers.get("X-Forwarded-For").and_then(|hv| Some(String::from(hv.to_str().unwrap()))); - if ip_repr.is_none() { - let connect_ip = connect_info.ip().to_string(); - ip_repr = Some(connect_ip.to_string()); + return Ok((convert_epoch(epoch_int.unwrap()), split_path.1.parse().unwrap())); } - context.insert("text", &ip_repr.unwrap()); - let data = TEMPLATES.render("basic.svg", &context); + let epoch_int = path.parse::(); + if epoch_int.is_err() { + return Err("Epoch is not a valid integer.".to_string()); + } + Ok( + (convert_epoch(epoch_int.unwrap()), String::from("svg")) + ) +} + +// basic handler that responds with a static string +async fn root_handler(Path(path): Path) -> impl IntoResponse { + let renderer = svg::Renderer::new(); + let mut context = Context::new(); + let f = Formatter::new(); + + let parse_result = parse_path(path); + if parse_result.is_err() { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::from(parse_result.err().unwrap())) + .unwrap(); + } + let (epoch, extension) = parse_result.unwrap(); + + context.insert("text", &f.convert(epoch.elapsed().ok().unwrap())); + context.insert("width", "512"); + context.insert("height", "34"); + + let data = TEMPLATES.render("basic.svg", &context); if data.is_err() { return Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) @@ -78,20 +110,35 @@ async fn root_handler(connect_info: ConnectInfo, headers: HeaderMap) .unwrap(); } - let raw_image = renderer.render(data.unwrap().into_bytes()); + match extension.as_str() { + "svg" => { + Response::builder() + .header(header::CONTENT_TYPE, "image/svg+xml") + .body(Full::from(data.unwrap())) + .unwrap() + } + "png" => { + let raw_image = renderer.render(data.unwrap().into_bytes()); + if raw_image.is_err() { + return Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Full::from( + format!("Internal Server Error :: {}", raw_image.err().unwrap()) + )) + .unwrap(); + } - if raw_image.is_err() { - return Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Full::from( - format!("Internal Server Error :: {}", raw_image.err().unwrap()) - )) - .unwrap(); + Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, "image/x-png") + .body(Full::from(raw_image.unwrap())) + .unwrap() + } + _ => { + Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::from("Unsupported extension.")) + .unwrap() + } } - - Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, "image/x-png") - .body(Full::from(raw_image.unwrap())) - .unwrap() } \ No newline at end of file diff --git a/src/templates/basic.svg b/src/templates/basic.svg index 5e81415..59b67f6 100644 --- a/src/templates/basic.svg +++ b/src/templates/basic.svg @@ -1,3 +1,6 @@ - - {{ text }} + + {{ text }} + \ No newline at end of file