diff --git a/Cargo.lock b/Cargo.lock index 11c091f..771a2de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,19 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "atom_syndication" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571832dcff775e26562e8e6930cd483de5587301d40d3a3b85d532b6383e15a7" +dependencies = [ + "chrono", + "derive_builder", + "diligent-date-parser", + "never", + "quick-xml", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -255,6 +268,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -338,6 +360,15 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "diligent-date-parser" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cf7fe294274a222363f84bcb63cdea762979a0443b4cf1f4f8fd17c86b1182" +dependencies = [ + "chrono", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -359,6 +390,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "entities" version = "1.0.1" @@ -459,8 +499,10 @@ dependencies = [ "include_dir", "maud", "orgize", + "rss", "serde", "serde_dhall", + "time", "worker", ] @@ -703,6 +745,12 @@ dependencies = [ "adler", ] +[[package]] +name = "never" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" + [[package]] name = "nom" version = "7.1.3" @@ -902,6 +950,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pretty" version = "0.11.3" @@ -946,6 +1000,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "encoding_rs", + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -999,6 +1063,18 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rss" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b2c77eb4450d7d5f98df52c381cd6c4e19b75dad9209a9530b85a44510219a" +dependencies = [ + "atom_syndication", + "derive_builder", + "never", + "quick-xml", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1156,6 +1232,36 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index fe83c34..0692cd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ include_dir = "0.7.3" frontmatter = "0.4.0" comrak = { version = "0.21.0", default-features = false } orgize = { git = "https://git.gmem.ca/arch/orgize.git", branch = "org-images", default-features = false } +rss = "2.0.6" +time = { version = "0.3.31", features = ["parsing", "formatting", "macros", "wasm-bindgen"] } [profile.release] lto = true @@ -23,4 +25,4 @@ codegen-units = 1 opt-level = 's' [package.metadata.wasm-pack.profile.release] -wasm-opt = false \ No newline at end of file +wasm-opt = false diff --git a/src/lib.rs b/src/lib.rs index b721d2d..7963359 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use worker::*; use maud::{html, Markup, Render, DOCTYPE}; use serde::{Serialize, Deserialize}; use include_dir::{include_dir, Dir}; +use time::{self, macros::format_description, format_description::well_known::Rfc2822}; mod posts; @@ -152,6 +153,50 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result { cache.put(key, res.cloned()?).await?; return Ok(res); }) + .get_async("/rss", |req, ctx| async move { + let cache = Cache::default(); + let key = req.url()?.to_string(); + if let Some(mut res) = cache.get(&key, false).await? { + let headers = res.headers_mut(); + let _ = headers.set("cf-cache", "hit"); + return Ok(res) + } + + let index = ctx.env.d1("INDEX")?; + let statement = index.prepare("SELECT * FROM posts ORDER BY date DESC"); + let posts: Vec = statement.all().await?.results()?; + let rss_posts: Vec = posts.into_iter().map(|p| { + let date = format!("{} 00:00:00 +00:00:00", p.date); + let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]:[offset_second]"); + let parsed_date = match time::OffsetDateTime::parse(&date, &format) { + Ok(r) => r, + Err(_e) => { time::OffsetDateTime::now_utc() }, + }; + let pub_date = match parsed_date.format(&Rfc2822) { + Ok(r) => r, + Err(_e) => { "".to_owned() }, + }; + rss::ItemBuilder::default() + .title(Some(p.title)) + .link(Some(format!("https://gabrielsimmer.com/blog/{}", p.slug))) + .pub_date(Some(pub_date)) + .build() + }).collect(); + let channel = rss::ChannelBuilder::default() + .title("Gabriel Simmer's Blog".to_owned()) + .link("https://gabrielsimmer.com/blog".to_owned()) + .description("Gabriel Simmer's Blog Posts.".to_owned()) + .items(rss_posts) + .build(); + + let mut res = Response::ok(channel.to_string())?; + let headers = res.headers_mut(); + let _ = headers.set("Cache-Control", "public, max-age=14400"); + let _ = headers.set("Content-Type", "application/rss+xml"); + + cache.put(key, res.cloned()?).await?; + Ok(res) + }) .get_async("/api/reindex", |req, ctx| async move { let auth_header = req.headers().get("Authorization")?; if auth_header.is_none() {