From 4ca3c83d447fbf86a4f21d841a107cb28a64658e Mon Sep 17 00:00:00 2001 From: Vika Date: Thu, 13 Mar 2025 11:06:23 +0300 Subject: WIP: RSS feed generator Some people are old-fashioned, and RSS feeds can be consumed even by laypeople unfamiliar with microformats2. This does violate DRY at first glance, but since the feeds are dynamically generated it's not repetition but rather format conversion, and as such does not violate DRY per se. --- src/frontend/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) (limited to 'src/frontend/mod.rs') diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 9ba1a69..478a48e 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -7,11 +7,14 @@ use axum::{ use axum_extra::{extract::Host, headers::HeaderMapExt}; use futures_util::FutureExt; use serde::Deserialize; -use std::convert::TryInto; +use std::{convert::TryInto, str::FromStr}; use tracing::{debug, error}; //pub mod login; pub mod onboarding; +mod rss_conversion; +use rss_conversion::feed_to_rss; + use kittybox_frontend_renderer::{ Entry, Feed, VCard, ErrorPage, Template, MainPage, @@ -22,6 +25,8 @@ pub use kittybox_frontend_renderer::assets::statics; #[derive(Debug, Deserialize)] pub struct QueryParams { after: Option, + #[serde(default)] + rss: bool, } #[derive(Debug)] @@ -297,7 +302,8 @@ pub async fn homepage( cursor: cursor.as_deref(), webring: crate::database::settings::Setting::into_inner(webring) } - .to_string(), + .to_string(), + rss_link: Some(&(feed_path + "?rss=true")) } .to_string(), ).into_response() @@ -333,6 +339,7 @@ pub async fn homepage( msg: Some(err.msg().to_string()), } .to_string(), + rss_link: None, } .to_string(), ).into_response() @@ -357,7 +364,7 @@ pub async fn catchall( .unwrap(); match get_post_from_database(&db, path.as_str(), query.after, user).await { - Ok((post, cursor)) => { + Ok((mut post, cursor)) => { let (blogname, channels) = tokio::join!( db.get_setting::(&host) .map(Result::unwrap_or_default), @@ -365,22 +372,18 @@ pub async fn catchall( db.get_channels(&host).map(|i| i.unwrap_or_default()) ); let mut headers = axum::http::HeaderMap::new(); - headers.insert( - axum::http::header::CONTENT_TYPE, - axum::http::HeaderValue::from_static(r#"text/html; charset="utf-8""#), - ); + headers.typed_insert(axum_extra::headers::ContentType::html()); headers.insert( axum::http::header::X_CONTENT_TYPE_OPTIONS, axum::http::HeaderValue::from_static("nosniff") ); if user.is_some() { - headers.insert( - axum::http::header::CACHE_CONTROL, - axum::http::HeaderValue::from_static("private") - ); + headers.typed_insert(axum_extra::headers::CacheControl::new().with_private()); } - if post["type"][0].as_str() == Some("h-entry") { + let post_type = post.pointer("/type/0").and_then(|i| i.as_str()); + + if post_type == Some("h-entry") { let last_modified = post["properties"]["updated"] .as_array() .and_then(|v| v.last()) @@ -400,6 +403,26 @@ pub async fn catchall( } } + if query.rss { + match post_type { + Some("h-feed") => { + headers.typed_insert(axum_extra::headers::ContentType::from_str("application/rss+xml").unwrap()); + return ( + StatusCode::OK, + headers, + feed_to_rss(post).to_string() + ).into_response() + }, + _ => { + headers.typed_insert(axum_extra::headers::ContentType::text_utf8()); + return (StatusCode::NOT_FOUND, headers, "RSS feeds cannot be generated for this document.").into_response() + } + } + } + let rss_link: Option = match post_type { + Some("h-feed") => Some(post["properties"]["uid"][0].as_str().unwrap().to_owned() + "?rss=true"), + _ => None + }; // Render the homepage ( StatusCode::OK, @@ -409,7 +432,7 @@ pub async fn catchall( blog_name: blogname.as_ref(), feeds: channels, user: session.as_deref(), - content: match post.pointer("/type/0").and_then(|i| i.as_str()) { + content: match post_type { Some("h-entry") => Entry { post: &post, from_feed: false, }.to_string(), Some("h-feed") => Feed { feed: &post, cursor: cursor.as_deref() }.to_string(), Some("h-card") => VCard { card: &post }.to_string(), @@ -417,6 +440,7 @@ pub async fn catchall( unimplemented!("Template for MF2-JSON type {:?}", unknown) } }, + rss_link: rss_link.as_deref() } .to_string(), ).into_response() @@ -443,7 +467,8 @@ pub async fn catchall( code: err.code(), msg: Some(err.msg().to_owned()), } - .to_string(), + .to_string(), + rss_link: None, } .to_string(), ).into_response() -- cgit 1.4.1