diff options
author | Tyler Murphy <tylerm@tylerm.dev> | 2023-07-08 23:30:33 -0400 |
---|---|---|
committer | Tyler Murphy <tylerm@tylerm.dev> | 2023-07-08 23:30:33 -0400 |
commit | ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094 (patch) | |
tree | 106ea2345204a70bf6457e9ba8e1e33a1442cb5e /src | |
download | bucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.tar.gz bucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.tar.bz2 bucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.zip |
bucket
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 68 | ||||
-rw-r--r-- | src/routes.rs | 57 |
2 files changed, 125 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8ea0114 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,68 @@ +use std::{env, error::Error, collections::HashMap, process::exit, sync::Arc}; +use serde::Deserialize; +use axum::{Router, Extension}; +use tera::Tera; + +mod routes; + +pub struct State { + pub templates: Tera, + pub pages: HashMap<String, (String, String)> +} + +#[derive(Deserialize)] +pub struct Page { + name: String, + url: String +} + +async fn load_state() -> Result<State, Box<dyn Error>> { + let mut templates = Tera::new("public/templates/**")?; + templates.autoescape_on(vec![]); + + let json = reqwest::get("https://raw.githubusercontent.com/bucketfish/bucket-webring/master/webring.json") + .await? + .json::<Vec<Page>>() + .await?; + + let mut pages = HashMap::new(); + + for (i, page) in json.iter().enumerate() { + let prev = json[(i + json.len() - 1) % json.len()].url.clone(); + let next = json[(i + json.len() + 1) % json.len()].url.clone(); + pages.insert(page.name.clone(), (prev, next)); + } + + Ok(State{ templates, pages }) +} + +#[tokio::main] +async fn main() { + + let port: u16 = env::var("PORT") + .unwrap_or_else(|_| String::new()) + .parse::<u16>() + .unwrap_or(8080); + + let addr = format!("[::]:{port}") + .parse::<std::net::SocketAddr>() + .expect("Failed to create port binding"); + + let state = match load_state().await { + Ok(s) => s, + Err(e) => { + eprintln!("Parsing error: {e}"); + exit(1); + } + }; + + let router = Router::new() + .fallback_service(routes::router()) + .layer(Extension(Arc::new(state))); + + axum::Server::bind(&addr) + .serve(router.into_make_service()) + .await + .expect("Failed to launch web server"); + +} diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 0000000..b8f855a --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,57 @@ +use std::sync::Arc; +use axum::{Router, response::{Response, IntoResponse}, extract::Query, Extension, http::{HeaderName, HeaderValue}, routing::get}; +use serde::Deserialize; +use tera::{Context, Tera}; +use tower_http::services::ServeDir; +use crate::State; + +pub fn router() -> Router { + Router::new() + .fallback(not_found) + .route("/", get(embed)) + .nest_service("/static", ServeDir::new("public/static")) +} + +#[derive(Deserialize, Debug)] +struct Params { + name: String, + theme: Option<String> +} + +async fn not_found() -> Response { + "do not the".into_response() +} + +async fn embed(Extension(state): Extension<Arc<State>>, Query(params): Query<Params>) -> Response { + let Some(page) = state.pages.get(¶ms.name) else { + return not_found().await + }; + + let theme = match ¶ms.theme { + Some(t) => t, + None => "dark" + }; + + let mut ctx = Context::default(); + ctx.insert("prev", page.0.as_str()); + ctx.insert("next", page.1.as_str()); + ctx.insert("theme", theme); + + render(&state.templates, &ctx, "embed.html") +} + +pub fn render(tera: &Tera, ctx: &Context, template: &str) -> Response { + let rendered = match tera.render(template, ctx) { + Ok(r) => r, + Err(err) => { + return format!("{err:?}").into_response() + } + }; + let mut res = rendered.into_response(); + res.headers_mut().insert( + HeaderName::from_static("content-type"), + HeaderValue::from_static("text/html") + ); + res +} + |