summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-07-08 23:30:33 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-07-08 23:30:33 -0400
commitccf6bef34ceaae6e28dacdb18b6eebdf67ad5094 (patch)
tree106ea2345204a70bf6457e9ba8e1e33a1442cb5e /src
downloadbucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.tar.gz
bucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.tar.bz2
bucket-ccf6bef34ceaae6e28dacdb18b6eebdf67ad5094.zip
bucket
Diffstat (limited to 'src')
-rw-r--r--src/main.rs68
-rw-r--r--src/routes.rs57
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(&params.name) else {
+ return not_found().await
+ };
+
+ let theme = match &params.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
+}
+