diff options
| author | Christine Dodrill <me@christine.website> | 2020-07-16 15:32:30 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-16 15:32:30 -0400 |
| commit | 385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7 (patch) | |
| tree | af789f7250519b23038a7e5ea0ae7f4f4c1ffdfc /src/app.rs | |
| parent | 449e934246c82d90dd0aac2644d67f928befeeb4 (diff) | |
| download | xesite-385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7.tar.xz xesite-385d25c9f96c0acd5d932488e3bd0ed36ceb4dd7.zip | |
Rewrite site backend in Rust (#178)
* add shell.nix changes for Rust #176
* set up base crate layout
* add first set of dependencies
* start adding basic app modules
* start html templates
* serve index page
* add contact and feeds pages
* add resume rendering support
* resume cleanups
* get signalboost page working
* rewrite config to be in dhall
* more work
* basic generic post loading
* more tests
* initial blog index support
* fix routing?
* render blogposts
* X-Clacks-Overhead
* split blog handlers into blog.rs
* gallery index
* gallery posts
* fix hashtags
* remove instantpage (it messes up the metrics)
* talk support + prometheus
* Create rust.yml
* Update rust.yml
* Update codeql-analysis.yml
* add jsonfeed library
* jsonfeed support
* rss/atom
* go mod tidy
* atom: add posted date
* rss: add publishing date
* nix: build rust program
* rip out go code
* rip out go templates
* prepare for serving in docker
* create kubernetes deployment
* create automagic deployment
* build docker images on non-master
* more fixes
* fix timestamps
* fix RSS/Atom/JSONFeed validation errors
* add go vanity import redirecting
* templates/header: remove this
* atom feed: fixes
* fix?
* fix??
* fix rust tests
* Update rust.yml
* automatically show snow during the winter
* fix dates
* show commit link in footer
* sitemap support
* fix compiler warning
* start basic patreon client
* integrate kankyo
* fix patreon client
* add patrons page
* remove this
* handle patron errors better
* fix build
* clean up deploy
* sort envvars for deploy
* remove deps.nix
* shell.nix: remove go
* update README
* fix envvars for tests
* nice
* blog: add rewrite in rust post
* blog/site-update: more words
Diffstat (limited to 'src/app.rs')
| -rw-r--r-- | src/app.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..7b5b377 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,191 @@ +use crate::{post::Post, signalboost::Person}; +use anyhow::Result; +use atom_syndication as atom; +use comrak::{markdown_to_html, ComrakOptions}; +use serde::Deserialize; +use std::{fs, path::PathBuf}; + +#[derive(Clone, Deserialize)] +pub struct Config { + #[serde(rename = "clackSet")] + clack_set: Vec<String>, + signalboost: Vec<Person>, + port: u16, + #[serde(rename = "resumeFname")] + resume_fname: PathBuf, +} + +pub fn markdown(inp: &str) -> String { + let mut options = ComrakOptions::default(); + + options.extension.autolink = true; + options.extension.table = true; + options.extension.description_lists = true; + options.extension.superscript = true; + options.extension.strikethrough = true; + options.extension.footnotes = true; + + options.render.unsafe_ = true; + + markdown_to_html(inp, &options) +} + +async fn patrons() -> Result<Option<patreon::Users>> { + use patreon::*; + let creds: Credentials = envy::prefixed("PATREON_").from_env().unwrap(); + let cli = Client::new(creds); + + match cli.campaign().await { + Ok(camp) => { + let id = camp.data[0].id.clone(); + + match cli.pledges(id).await { + Ok(users) => Ok(Some(users)), + Err(why) => { + log::error!("error getting pledges: {:?}", why); + Ok(None) + } + } + } + Err(why) => { + log::error!("error getting patreon campaign: {:?}", why); + Ok(None) + } + } +} + +pub const ICON: &'static str = "https://christine.website/static/img/avatar.png"; + +pub struct State { + pub cfg: Config, + pub signalboost: Vec<Person>, + pub resume: String, + pub blog: Vec<Post>, + pub gallery: Vec<Post>, + pub talks: Vec<Post>, + pub everything: Vec<Post>, + pub jf: jsonfeed::Feed, + pub rf: rss::Channel, + pub af: atom::Feed, + pub sitemap: Vec<u8>, + pub patrons: Option<patreon::Users>, +} + +pub async fn init(cfg: PathBuf) -> Result<State> { + let cfg: Config = serde_dhall::from_file(cfg).parse()?; + let sb = cfg.signalboost.clone(); + let resume = fs::read_to_string(cfg.resume_fname.clone())?; + let resume: String = markdown(&resume); + let blog = crate::post::load("blog")?; + let gallery = crate::post::load("gallery")?; + let talks = crate::post::load("talks")?; + let mut everything: Vec<Post> = vec![]; + + { + let blog = blog.clone(); + let gallery = gallery.clone(); + let talks = talks.clone(); + everything.extend(blog.iter().cloned()); + everything.extend(gallery.iter().cloned()); + everything.extend(talks.iter().cloned()); + }; + + everything.sort(); + everything.reverse(); + + let mut ri: Vec<rss::Item> = vec![]; + let mut ai: Vec<atom::Entry> = vec![]; + + let mut jfb = jsonfeed::Feed::builder() + .title("Christine Dodrill's Blog") + .description("My blog posts and rants about various technology things.") + .author( + jsonfeed::Author::new() + .name("Christine Dodrill") + .url("https://christine.website") + .avatar(ICON), + ) + .feed_url("https://christine.website/blog.json") + .user_comment("This is a JSON feed of my blogposts. For more information read: https://jsonfeed.org/version/1") + .home_page_url("https://christine.website") + .icon(ICON) + .favicon(ICON); + + for post in &everything { + let post = post.clone(); + jfb = jfb.item(post.clone().into()); + ri.push(post.clone().into()); + ai.push(post.clone().into()); + } + + let af = { + let mut af = atom::FeedBuilder::default(); + af.title("Christine Dodrill's Blog"); + af.id("https://christine.website/blog"); + af.generator({ + let mut generator = atom::Generator::default(); + generator.set_value(env!("CARGO_PKG_NAME")); + generator.set_version(env!("CARGO_PKG_VERSION").to_string()); + generator.set_uri("https://github.com/Xe/site".to_string()); + + generator + }); + af.entries(ai); + + af.build().unwrap() + }; + + let rf = { + let mut rf = rss::ChannelBuilder::default(); + rf.title("Christine Dodrill's Blog"); + rf.link("https://christine.website/blog"); + rf.generator(crate::APPLICATION_NAME.to_string()); + rf.items(ri); + + rf.build().unwrap() + }; + + let mut sm: Vec<u8> = vec![]; + let smw = sitemap::writer::SiteMapWriter::new(&mut sm); + let mut urlwriter = smw.start_urlset()?; + for url in &[ + "https://christine.website/resume", + "https://christine.website/contact", + "https://christine.website/", + "https://christine.website/blog", + "https://christine.website/signalboost", + ] { + urlwriter.url(*url)?; + } + + for post in &everything { + urlwriter.url(format!("https://christine.website/{}", post.link))?; + } + + urlwriter.end()?; + + Ok(State { + cfg: cfg, + signalboost: sb, + resume: resume, + blog: blog, + gallery: gallery, + talks: talks, + everything: everything, + jf: jfb.build(), + af: af, + rf: rf, + sitemap: sm, + patrons: patrons().await?, + }) +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + #[tokio::test] + async fn init() -> Result<()> { + super::init("./config.dhall".into()).await?; + Ok(()) + } +} |
