diff --git a/Cargo.lock b/Cargo.lock index 95a1340..583bf3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,6 +661,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "futures" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.17" @@ -668,6 +683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -676,6 +692,36 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +[[package]] +name = "futures-executor" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.17" @@ -695,11 +741,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" dependencies = [ "autocfg", + "futures-channel", "futures-core", + "futures-io", + "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] @@ -1038,6 +1091,7 @@ dependencies = [ "chrono", "config", "derive_more", + "futures", "graphql_client", "lazy_static", "log", @@ -1049,7 +1103,6 @@ dependencies = [ "serde_json", "sled", "url", - "urlencoding", ] [[package]] @@ -1389,6 +1442,12 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + [[package]] name = "proc-macro2" version = "1.0.26" @@ -2122,12 +2181,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "urlencoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" - [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index b2aca2e..304e1d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,4 @@ chrono = "0.4.19" sled = "0.34.7" bincode = "1.3.3" -urlencoding = "2.1.0" +futures = "0.3.17" diff --git a/src/proxy.rs b/src/proxy.rs index ab80ff7..c252673 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -17,9 +17,9 @@ use std::ops::{Bound, RangeBounds}; use actix_web::{http::header, web, HttpResponse, Responder}; -use reqwest::header::{CONTENT_TYPE, USER_AGENT}; +use futures::future::join_all; +use reqwest::header::CONTENT_TYPE; use sailfish::TemplateOnce; -use serde::{Deserialize, Serialize}; use crate::data::PostResp; use crate::AppData; @@ -31,7 +31,6 @@ pub mod routes { pub index: &'static str, pub page: &'static str, pub asset: &'static str, - pub gist: &'static str, } impl Proxy { @@ -40,7 +39,6 @@ pub mod routes { index: "/", page: "/{username}/{post}", asset: "/asset/medium/{name}", - gist: "/asset/github-gist", } } pub fn get_page(&self, username: &str, post: &str) -> String { @@ -52,20 +50,12 @@ pub mod routes { pub fn get_medium_asset(&self, asset_name: &str) -> String { self.asset.replace("{name}", asset_name) } - - pub fn get_gist(&self, url: &str) -> String { - if let Some(gist_id) = url.split('/').last() { - format!("{}?gist={}", self.gist, urlencoding::encode(gist_id)) - } else { - url.to_owned() - } - } } } // credits @carlomilanesi: // https://users.rust-lang.org/t/how-to-get-a-substring-of-a-string/1351/11 -trait StringUtils { +pub trait StringUtils { fn substring(&self, start: usize, len: usize) -> &str; fn slice(&self, range: impl RangeBounds) -> &str; } @@ -121,6 +111,7 @@ impl StringUtils for str { pub struct Post { pub data: PostResp, pub id: String, + pub gists: Option>, } const INDEX: &str = include_str!("../templates/index.html"); @@ -152,104 +143,6 @@ async fn assets(path: web::Path, data: AppData) -> impl Responder { .body(res.bytes().await.unwrap()) } -#[derive(Deserialize, Serialize)] -struct GistQuery { - gist: String, -} - -#[derive(Deserialize, Serialize, TemplateOnce)] -#[template(path = "gist.html")] -#[template(rm_whitespace = true)] -pub struct GistContent { - pub files: Vec, - pub html_url: String, -} - -#[derive(TemplateOnce)] -#[template(path = "gist_error.html")] -#[template(rm_whitespace = true)] -pub struct GistContentError; - -#[derive(Deserialize, Serialize)] -pub struct GistFile { - pub file_name: String, - pub content: String, - pub language: String, - pub raw_url: String, -} - -impl GistFile { - pub fn get_html_content(&self) -> String { - let mut content = self.content.as_str(); - if self.content.starts_with('"') { - content = self.content.slice(1..); - } - - if content.ends_with('"') { - content = content.slice(..content.len() - 1); - } - content.replace("\\t", " ") - } -} - -#[my_codegen::get(path = "crate::V1_API_ROUTES.proxy.gist")] -async fn get_gist(query: web::Query, data: AppData) -> impl Responder { - const URL: &str = "https://api.github.com/gists/"; - let url = format!("{}{}", URL, query.gist); - - let resp = data - .client - .get(&url) - .header(USER_AGENT, "libmedium") - .send() - .await - .unwrap() - .json::() - .await - .unwrap(); - if let Some(files) = resp.get("files") { - if let serde_json::Value::Object(v) = files { - let mut files = Vec::with_capacity(v.len()); - v.iter().for_each(|(name, file_obj)| { - let file = GistFile { - file_name: name.to_string(), - content: file_obj - .get("content") - .unwrap() - .as_str() - .unwrap() - .to_owned(), - language: file_obj - .get("language") - .unwrap() - .as_str() - .unwrap() - .to_owned(), - raw_url: file_obj - .get("raw_url") - .unwrap() - .as_str() - .unwrap() - .to_owned(), - }; - files.push(file); - }); - let gist = GistContent { - files, - html_url: resp.get("html_url").unwrap().to_string(), - }; - - return HttpResponse::Ok() - .content_type("text/html") - .body(gist.render_once().unwrap()); - } - }; - let err = GistContentError {}; - HttpResponse::Ok() - .content_type("text/html") - .body(err.render_once().unwrap()) -} - #[my_codegen::get(path = "crate::V1_API_ROUTES.proxy.page")] async fn page(path: web::Path<(String, String)>, data: AppData) -> impl Responder { let post_id = path.1.split('-').last(); @@ -258,12 +151,41 @@ async fn page(path: web::Path<(String, String)>, data: AppData) -> impl Responde } let id = post_id.unwrap(); + let post_data = data.get_post(id).await; + let mut futs = Vec::new(); + let paragraphs = &post_data.content.body_model.paragraphs; + + for p in paragraphs.iter() { + if p.type_ == "IFRAME" { + let src = &p + .iframe + .as_ref() + .unwrap() + .media_resource + .as_ref() + .unwrap() + .href; + if src.contains("gist.github.com") { + let gist_id = post_data.get_gist_id(&src); + let fut = data.get_gist(gist_id.to_owned()); + futs.push(fut); + } + } + } + let gists = if futs.is_empty() { + None + } else { + let x = join_all(futs).await; + Some(x) + }; + let page = Post { id: id.to_owned(), - data: data.get_post(id).await, - } - .render_once() - .unwrap(); + data: post_data, + gists, + }; + + let page = page.render_once().unwrap(); HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(page) @@ -271,7 +193,6 @@ async fn page(path: web::Path<(String, String)>, data: AppData) -> impl Responde pub fn services(cfg: &mut web::ServiceConfig) { cfg.service(assets); - cfg.service(get_gist); cfg.service(page); cfg.service(index); } diff --git a/templates/gist.html b/templates/gist.html deleted file mode 100644 index 1ee893c..0000000 --- a/templates/gist.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Gist - - - <. for file in files {.> - <.= file.get_html_content() .> - - <.}.> - See on GitHub - - - diff --git a/templates/gist_error.html b/templates/gist_error.html deleted file mode 100644 index 6d9fedf..0000000 --- a/templates/gist_error.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Gist - - - Error - - - diff --git a/templates/gist_insert.html b/templates/gist_insert.html new file mode 100644 index 0000000..3d5d840 --- /dev/null +++ b/templates/gist_insert.html @@ -0,0 +1,8 @@ +<. let gist_id = data.get_gist_id(&src); .> +<. let (_, gist)= gists.as_ref().unwrap().iter().find(|(id, _)| id == gist_id).as_ref().unwrap(); .> +<. for file in &gist.files {.> + <.= file.get_html_content() .> +<.}.> +See gist on GitHub diff --git a/templates/main.css b/templates/main.css index 2c11008..d5c8112 100644 --- a/templates/main.css +++ b/templates/main.css @@ -63,9 +63,9 @@ code { font-family: monospace; font-size: 15px; white-space: pre-wrap; - background-color: #888; - color: #fff; + display: block; font-weight: 600; + line-height: 1rem; } iframe { diff --git a/templates/post.html b/templates/post.html index 1e2a314..4a0b31b 100644 --- a/templates/post.html +++ b/templates/post.html @@ -23,7 +23,7 @@ on <.= &date .> · <.= data.reading_time.floor() as usize .> min read

- <. let paragraphs = data.content.body_model.paragraphs; .> + <. let paragraphs = &data.content.body_model.paragraphs; .> <. for (pindex, p) in paragraphs.iter().enumerate() {.> <. if pindex == 0 && p.type_ == "H3" {.> <. continue; .> @@ -45,8 +45,11 @@ <.} else if p.type_ == "IFRAME" {.> <. let src = &p.iframe.as_ref().unwrap().media_resource.as_ref().unwrap().href; .> <. if src.contains("gist.github.com"){.> - + <. include!("./gist_insert.html"); .> + <.} else {.> <.}.>