1
Fork 0

Create webfinger crate #5

Merged
steffo merged 53 commits from feature/webfinger into main 2024-11-16 06:33:06 +00:00
4 changed files with 165 additions and 5 deletions
Showing only changes of commit c83fdc43e6 - Show all commits

View file

@ -11,6 +11,7 @@ axum = { version = "0.7.7", features = ["macros"] }
axum-extra = { version = "0.9.4", features = ["query"] } axum-extra = { version = "0.9.4", features = ["query"] }
log = "0.4.22" log = "0.4.22"
micronfig = "0.3.0" micronfig = "0.3.0"
minijinja = "2.5.0"
pretty_env_logger = "0.5.0" pretty_env_logger = "0.5.0"
quick-xml = { version = "0.37.0", features = ["serialize"] } quick-xml = { version = "0.37.0", features = ["serialize"] }
serde = { version = "1.0.215", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] }

View file

@ -1,4 +1,6 @@
use std::sync::Arc;
use anyhow::Context; use anyhow::Context;
use axum::Extension;
mod config; mod config;
mod route; mod route;
@ -8,10 +10,18 @@ mod route;
async fn main() -> anyhow::Result<std::convert::Infallible> { async fn main() -> anyhow::Result<std::convert::Infallible> {
pretty_env_logger::init(); pretty_env_logger::init();
log::debug!("Logging initialized!"); log::debug!("Logging initialized!");
log::trace!("Creating Minijinja environment...");
let mut mj = minijinja::Environment::<'static>::new();
log::trace!("Adding webfinger page to the Minijinja environment...");
mj.add_template("webfinger.html.j2", include_str!("webfinger.html.j2"))
.expect("webfinger.html.j2 to be a valid Minijinja template");
log::trace!("Creating Axum router..."); log::trace!("Creating Axum router...");
let app = axum::Router::new() let app = axum::Router::new()
.route("/.well-known/webfinger", axum::routing::get(route::webfinger_handler)); .route("/.well-known/webfinger", axum::routing::get(route::webfinger_handler))
.layer(Extension(Arc::new(mj)));
log::trace!("Axum router created successfully!"); log::trace!("Axum router created successfully!");
log::trace!("Creating Tokio listener..."); log::trace!("Creating Tokio listener...");

View file

@ -1,3 +1,5 @@
use std::sync::Arc;
use axum::Extension;
use axum::http::{HeaderMap, Response, StatusCode}; use axum::http::{HeaderMap, Response, StatusCode};
use axum_extra::extract::Query; use axum_extra::extract::Query;
use serde::Deserialize; use serde::Deserialize;
@ -22,6 +24,7 @@ const WEBFINGER_DOC: &str = "/.well-known/webfinger";
pub async fn webfinger_handler( pub async fn webfinger_handler(
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>, Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>,
headers: HeaderMap, headers: HeaderMap,
Extension(mj): Extension<Arc<minijinja::Environment<'static>>>,
) -> Result<Response<String>, StatusCode> { ) -> Result<Response<String>, StatusCode> {
log::info!("Handling a WebFinger request!"); log::info!("Handling a WebFinger request!");
@ -142,7 +145,7 @@ pub async fn webfinger_handler(
body.push_str(&json); body.push_str(&json);
} }
return Ok(response) return Ok(response);
}, },
"application/xml" | "application/xrd+xml" => { "application/xml" | "application/xrd+xml" => {
let subject = Some(resource); let subject = Some(resource);
@ -196,15 +199,63 @@ pub async fn webfinger_handler(
{ {
let body = response.body_mut(); let body = response.body_mut();
body.push_str("<?xml version='1.0' encoding='UTF-8'?>"); body.push_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
body.push_str(&xml); body.push_str(&xml);
} }
return Ok(response) return Ok(response);
},
"text/html" => {
let aliases: Vec<String> = aliases.into_iter()
.map(|alias| alias.alias)
.collect();
let properties: Vec<(String, Option<String>)> = properties.into_iter()
.map(|prop| {
(prop.rel, prop.value)
})
.collect();
let links: Vec<(String, Option<String>, Option<String>, Option<String>, Vec<(String, Option<String>)>, Vec<(String, String)>)> = links_full
.into_iter()
.map(|(link, properties, titles)| {
(
link.rel,
link.type_,
link.href,
link.template,
properties.into_iter()
.map(|prop| (prop.rel, prop.value))
.collect::<Vec<(String, Option<String>)>>(),
titles.into_iter()
.map(|title| (title.language, title.value))
.collect::<Vec<(String, String)>>()
)
})
.collect();
let html = mj.get_template("webfinger.html.j2")
.expect("webfinger.html.j2 to exist")
.render(
minijinja::context!(
subject => resource,
aliases => aliases,
properties => properties,
links => links,
)
)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
{
let body = response.body_mut();
body.push_str(&html);
}
return Ok(response);
}, },
_ => { _ => {
continue; continue;
} },
} }
} }

View file

@ -0,0 +1,98 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>{{ subject }} · Acrate Webfinger</title>
</head>
<body>
<h1>
Acrate Webfinger
</h1>
<h2 id="section-subject">
<span>{{ subject }}</span>
</h2>
{% if aliases %}
<section id="section-aliases">
<h3>
Aliases
</h3>
<ul>
{% for alias in aliases %}
<li><span>{{ alias }}</span></li>
{% endfor %}
</ul>
</section>
{% endif %}
{% if properties %}
<section id="section-properties">
<h3>
Properties
</h3>
<dl>
{% for property in properties %}
<dt>
<a href="{{ property[0] }}">{{ property[0] }}</a>
</dt>
<dd>
<span>{{ property[1] }}</span>
</dd>
{% endfor %}
</dl>
</section>
{% endif %}
{% if links %}
<section id="section-links">
<h3>
Links
</h3>
<dl>
{% for link in links %}
<dt>
<h4>
<a href="{{ link[0] }}">{{ link[0] }}</a>{% if link[1] is not none %} <small>(<span>{{ link[1] }}</span>)</small>{% endif %}
</h4>
</dt>
<dd>
<h5>
Link destination
</h5>
{% if link[2] is not none %}
<a href="{{ link[2] }}">{{ link[2] }}</a>
{% elif link[3] is not none %}
<span>{{ link[3] }}</span>
{% endif %}
{% if link[4] %}
<h5>
Link properties
</h5>
<dl>
{% for property in link[4] %}
<dt>
<a href="{{ property[0] }}">{{ property[0] }}</a>
</dt>
<dd>
<span>{{ property[1] }}</span>
</dd>
{% endfor %}
</dl>
{% endif %}
{% if link[5] %}
<h5>
Link titles
</h5>
<dl>
{% for title in link[5] %}
<dt>
<span>{{ title[0] }}</span>
</dt>
<dd>
<span>{{ title[1] }}</span>
</dd>
{% endfor %}
</dl>
{% endif %}
</dd>
{% endfor %}
</dl>
</section>
{% endif %}
</body>