mirror of
https://github.com/Xevion/time-banner.git
synced 2025-12-06 07:16:36 -06:00
Switch to relative time, path parsing, SVG templating
This commit is contained in:
72
Cargo.lock
generated
72
Cargo.lock
generated
@@ -188,7 +188,10 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"time",
|
||||||
|
"wasm-bindgen",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -200,7 +203,7 @@ checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz-build",
|
"chrono-tz-build",
|
||||||
"phf",
|
"phf 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -210,7 +213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
|
checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parse-zoneinfo",
|
"parse-zoneinfo",
|
||||||
"phf",
|
"phf 0.10.1",
|
||||||
"phf_codegen",
|
"phf_codegen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -461,7 +464,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -619,6 +622,15 @@ version = "0.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
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]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -736,7 +748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -869,7 +881,16 @@ version = "0.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
@@ -879,7 +900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_generator",
|
"phf_generator",
|
||||||
"phf_shared",
|
"phf_shared 0.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -888,7 +909,7 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_shared",
|
"phf_shared 0.10.0",
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -902,6 +923,15 @@ dependencies = [
|
|||||||
"uncased",
|
"uncased",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pico-args"
|
name = "pico-args"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -1365,6 +1395,17 @@ dependencies = [
|
|||||||
"once_cell",
|
"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]]
|
[[package]]
|
||||||
name = "time-banner"
|
name = "time-banner"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1379,11 +1420,22 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tera",
|
"tera",
|
||||||
|
"timeago",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"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]]
|
[[package]]
|
||||||
name = "tiny-skia"
|
name = "tiny-skia"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -1769,6 +1821,12 @@ dependencies = [
|
|||||||
"try-lock",
|
"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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
|||||||
@@ -19,3 +19,4 @@ dotenvy = "0.15.7"
|
|||||||
envy = "0.4.2"
|
envy = "0.4.2"
|
||||||
tera = "1.19.0"
|
tera = "1.19.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
timeago = "0.4.1"
|
||||||
|
|||||||
99
src/main.rs
99
src/main.rs
@@ -1,16 +1,18 @@
|
|||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use axum::{http::StatusCode, response::IntoResponse, Router, routing::{get}};
|
use axum::{http::StatusCode, response::IntoResponse, Router, routing::{get}};
|
||||||
use axum::body::{Full};
|
use axum::body::{Full};
|
||||||
use axum::extract::ConnectInfo;
|
use axum::extract::{ConnectInfo, Path};
|
||||||
use axum::http::{header, HeaderMap};
|
use axum::http::{header, HeaderMap};
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use config::Configuration;
|
use config::Configuration;
|
||||||
use tera::{Tera, Context};
|
use tera::{Tera, Context};
|
||||||
|
use timeago::Formatter;
|
||||||
|
|
||||||
mod svg;
|
mod svg;
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ async fn main() {
|
|||||||
.with_max_level(config.log_level())
|
.with_max_level(config.log_level())
|
||||||
.init();
|
.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));
|
let addr = SocketAddr::from((config.socket_addr(), config.port));
|
||||||
axum::Server::bind(&addr)
|
axum::Server::bind(&addr)
|
||||||
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
||||||
@@ -54,21 +56,51 @@ async fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic handler that responds with a static string
|
fn convert_epoch(epoch: u64) -> SystemTime {
|
||||||
async fn root_handler(connect_info: ConnectInfo<SocketAddr>, headers: HeaderMap) -> impl IntoResponse {
|
SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(epoch)
|
||||||
let renderer = svg::Renderer::new();
|
}
|
||||||
|
|
||||||
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::<u64>();
|
||||||
|
if epoch_int.is_err() {
|
||||||
|
return Err("Epoch is not a valid integer.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let mut ip_repr: Option<String> = headers.get("X-Forwarded-For").and_then(|hv| Some(String::from(hv.to_str().unwrap())));
|
return Ok((convert_epoch(epoch_int.unwrap()), split_path.1.parse().unwrap()));
|
||||||
if ip_repr.is_none() {
|
|
||||||
let connect_ip = connect_info.ip().to_string();
|
|
||||||
ip_repr = Some(connect_ip.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.insert("text", &ip_repr.unwrap());
|
let epoch_int = path.parse::<u64>();
|
||||||
let data = TEMPLATES.render("basic.svg", &context);
|
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<String>) -> 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() {
|
if data.is_err() {
|
||||||
return Response::builder()
|
return Response::builder()
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
@@ -78,20 +110,35 @@ async fn root_handler(connect_info: ConnectInfo<SocketAddr>, headers: HeaderMap)
|
|||||||
.unwrap();
|
.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() {
|
Response::builder()
|
||||||
return Response::builder()
|
.status(StatusCode::OK)
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
.header(header::CONTENT_TYPE, "image/x-png")
|
||||||
.body(Full::from(
|
.body(Full::from(raw_image.unwrap()))
|
||||||
format!("Internal Server Error :: {}", raw_image.err().unwrap())
|
.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()
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
<svg width="256" height="32" xmlns="http://www.w3.org/2000/svg" font-family="Arial" font-size="28">
|
<svg width="{{ width }}" height="{{ height }}" xmlns="http://www.w3.org/2000/svg" font-family="Roboto Mono" font-size="27">
|
||||||
<text x="10" y="28">{{ text }}</text>
|
<text x="8" y="27">{{ text }}</text>
|
||||||
|
<style>
|
||||||
|
text
|
||||||
|
</style>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 148 B After Width: | Height: | Size: 209 B |
Reference in New Issue
Block a user