From 53dffcd2a854a710af3e2f0cf302e0d825064656 Mon Sep 17 00:00:00 2001 From: cn-kali-team Date: Thu, 18 Jan 2024 17:40:02 +0800 Subject: [PATCH] atom.xml --- .editorconfig | 11 ++ src/html.rs | 167 +++++++++++++++++++++++++ src/lib.rs | 218 +++++--------------------------- src/page.rs | 339 +++++++++++++++++++++++++++++--------------------- 4 files changed, 411 insertions(+), 324 deletions(-) create mode 100644 .editorconfig create mode 100644 src/html.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5c0360f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +[*] +charset = utf-8 + + +[*.pest] +indent_style = space +indent_size = 2 + +[*.toml] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/src/html.rs b/src/html.rs new file mode 100644 index 0000000..6ae468f --- /dev/null +++ b/src/html.rs @@ -0,0 +1,167 @@ +pub const _REWRITER_HTTP: &str = r#" + "#; +pub const BASE: &str = r#" +
Powered by Kali-Team
+ "#; +pub const RESIZE: &str = r#" + "#; +pub const THEME: &str = r#" + "#; +pub const TOC: &str = r#" + "#; +pub const HEAD: &str = r#" + + "#; diff --git a/src/lib.rs b/src/lib.rs index ba8dc2a..7a794c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ +mod html; mod page; +use crate::html::{BASE, HEAD, RESIZE, THEME, TOC}; use crate::page::QueryBody; use lol_html::html_content::ContentType; use lol_html::{element, HtmlRewriter, Settings}; @@ -30,15 +32,15 @@ struct MobileData { is_push: bool, } -struct BlogEnv { - page_map: HashMap, - comment_map: HashMap, - my_domain: String, - notion_domain: String, - title: String, - description: String, - icon: String, - query_body: String, +pub struct BlogEnv { + pub page_map: HashMap, + pub comment_map: HashMap, + pub my_domain: String, + pub notion_domain: String, + pub title: String, + pub description: String, + pub icon: String, + pub query_body: String, } fn var_to_map(env: &Env, name: &str) -> HashMap { @@ -213,9 +215,9 @@ async fn rewriter_html(req: Request, full_url: Url, blog_env: BlogEnv) -> Result let mut response_header = response.headers().clone(); if let Ok(Some(mut csp)) = response_header.get("Content-Security-Policy") { csp = csp.replace( - "https://gist.github.com", - "https://gist.github.com https://giscus.app/client.js https://static.cloudflareinsights.com", - ); + "https://gist.github.com", + "https://gist.github.com https://giscus.app/client.js https://static.cloudflareinsights.com", + ); if csp.contains("style-src") { csp = csp.replace("style-src", "style-src https://giscus.app/default.css"); } else { @@ -345,173 +347,6 @@ fn rewriter( let mut output = vec![]; let title = title.unwrap_or(blog_env.description); let icon_url = icon_url.unwrap_or(blog_env.icon); - let _rewriter_http = r#" - "#; - let base = r#" -
Powered by Kali-Team
- "#; - let resize = r#" - "#; - let theme = r#" - "#; - let toc = r#" - "#; - let head = r#" - - "#; let mut rewriter = HtmlRewriter::new( Settings { element_content_handlers: vec![ @@ -555,15 +390,15 @@ fn rewriter( Ok(()) }), element!("head", |el| { - el.append(head, ContentType::Html); + el.append(HEAD, ContentType::Html); Ok(()) }), element!("body", |el| { // el.append(rewriter_http, ContentType::Html); - el.append(base, ContentType::Html); - el.append(resize, ContentType::Html); - el.append(theme, ContentType::Html); - el.append(toc, ContentType::Html); + el.append(BASE, ContentType::Html); + el.append(RESIZE, ContentType::Html); + el.append(THEME, ContentType::Html); + el.append(TOC, ContentType::Html); if !blog_env.comment_map.is_empty() { el.append(&get_comment(&blog_env.comment_map), ContentType::Html); } @@ -594,7 +429,7 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result { } "/sitemap.xml" => { let page = get_pages( - "queryCollection?src=reset", + "queryCollection?src=initial_load", &blog_env.notion_domain, &blog_env.query_body, ) @@ -603,6 +438,19 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result { let sitemap = page.get_sitemap().replace("MY_DOMAIN", &blog_env.my_domain); return Ok(Response::ok(sitemap)?.with_headers(header)); } + "/index.xml" => { + let page = get_pages( + "queryCollection?src=initial_load", + &blog_env.notion_domain, + &blog_env.query_body, + ) + .await?; + let header = Headers::from_iter(vec![("Content-Type", "text/xml")]); + let atom = page + .get_atom(&blog_env) + .replace("MY_DOMAIN", &blog_env.my_domain); + return Ok(Response::ok(atom)?.with_headers(header)); + } "/api/v3/teV1" => { return Response::ok("success"); } diff --git a/src/page.rs b/src/page.rs index c339fd7..990ee35 100644 --- a/src/page.rs +++ b/src/page.rs @@ -1,3 +1,4 @@ +use crate::BlogEnv; use chrono::{DateTime, FixedOffset}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -5,237 +6,297 @@ use std::collections::HashMap; #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct QueryBody { - requests: Vec, + requests: Vec, } impl QueryBody { - pub fn new(id: String) -> QueryBody { - QueryBody { - requests: vec![Requests { - pointer: Pointer { - table: "block".to_string(), - id, - }, - version: -1, - }], - } + pub fn new(id: String) -> QueryBody { + QueryBody { + requests: vec![Requests { + pointer: Pointer { + table: "block".to_string(), + id, + }, + version: -1, + }], } + } } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Requests { - pointer: Pointer, - version: i32, + pointer: Pointer, + version: i32, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Pointer { - table: String, - id: String, + table: String, + id: String, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct QueryCollection { - record_map: RecordMap, + record_map: RecordMap, } impl QueryCollection { - pub fn get_sitemap(&self) -> String { - let mut sitemap = String::from("\n\n"); - for (_key, block) in self.record_map.block.iter() { - sitemap.push_str(&block.value.get_page().unwrap_or_default()); - } - sitemap.push_str(""); - sitemap + pub fn get_sitemap(&self) -> String { + let mut sitemap = String::from("\n\n"); + for (_key, block) in self.record_map.block.iter() { + sitemap.push_str(&block.value.get_loc().unwrap_or_default()); } - pub fn get_title(&self, id: &uuid::Uuid) -> Option { - if let Some(block) = self.record_map.block.get(&id.to_string()) { - return block.value.get_title(); - } - None + sitemap.push_str(""); + sitemap + } + pub fn get_atom(&self, blog_env: &BlogEnv) -> String { + let mut atom = String::from("\n\n"); + atom.push_str(&format!("{}\n", blog_env.title)); + atom.push_str(&format!("{}\n", blog_env.description)); + atom.push_str(&format!( + "\n", + blog_env.my_domain + )); + atom.push_str(&format!( + "\n", + blog_env.my_domain + )); + atom.push_str(&format!("https://{}\n", blog_env.my_domain)); + atom.push_str(&format!( + "{}\n", + blog_env.description + )); + for (_key, block) in self.record_map.block.iter() { + atom.push_str(&block.value.get_atom().unwrap_or_default()); } - pub fn get_icon(&self, id: &uuid::Uuid) -> Option { - if let Some(block) = self.record_map.block.get(&id.to_string()) { - return block.value.get_icon(); - } - None + atom.push_str(""); + atom + } + pub fn get_title(&self, id: &uuid::Uuid) -> Option { + if let Some(block) = self.record_map.block.get(&id.to_string()) { + return block.value.get_title(); } + None + } + pub fn get_icon(&self, id: &uuid::Uuid) -> Option { + if let Some(block) = self.record_map.block.get(&id.to_string()) { + return block.value.get_icon(); + } + None + } } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct RecordMap { - block: HashMap, + block: HashMap, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Block { - value: BlockEnum, + value: BlockEnum, } impl BlockEnum { - fn get_page(&self) -> Option { - match self { - BlockEnum::Page(p) => Some(p.to_loc()), - _ => None, - } + fn get_loc(&self) -> Option { + match self { + BlockEnum::Page(p) => Some(p.to_loc()), + _ => None, } - fn get_title(&self) -> Option { - match self { - BlockEnum::Page(p) => Some(p.properties.title.get_title()), - _ => None, - } + } + fn get_atom(&self) -> Option { + match self { + BlockEnum::Page(p) => Some(p.to_entry()), + _ => None, } - fn get_icon(&self) -> Option { - match self { - BlockEnum::Page(p) => { - if let Ok(u) = worker::Url::parse(&p.format.page_icon) { - return Some(u.to_string()); - } else { - None - } - } - _ => None, + } + fn get_title(&self) -> Option { + match self { + BlockEnum::Page(p) => Some(p.properties.title.get_title()), + _ => None, + } + } + fn get_icon(&self) -> Option { + match self { + BlockEnum::Page(p) => { + if let Ok(u) = worker::Url::parse(&p.format.page_icon) { + Some(u.to_string()) + } else { + None } + } + _ => None, } + } } #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type", rename_all = "snake_case")] pub enum BlockEnum { - Page(Page), - ColumnList(ColumnList), - Column(Column), - CollectionView(CollectionView), - #[serde(other)] - Divider, + Page(Page), + ColumnList(ColumnList), + Column(Column), + CollectionView(CollectionView), + #[serde(other)] + Divider, } #[derive(Serialize, Deserialize, Debug)] pub struct CollectionView { - id: String, - version: i32, - view_ids: Vec, - #[serde(with = "date_format")] - created_time: DateTime, - #[serde(with = "date_format")] - last_edited_time: DateTime, + id: String, + version: i32, + view_ids: Vec, + #[serde(with = "date_format")] + created_time: DateTime, + #[serde(with = "date_format")] + last_edited_time: DateTime, } #[derive(Serialize, Deserialize, Debug)] pub struct Column { - id: String, - version: i32, - content: Vec, - #[serde(with = "date_format")] - created_time: DateTime, - #[serde(with = "date_format")] - last_edited_time: DateTime, + id: String, + version: i32, + content: Vec, + #[serde(with = "date_format")] + created_time: DateTime, + #[serde(with = "date_format")] + last_edited_time: DateTime, } #[derive(Serialize, Deserialize, Debug)] pub struct ColumnList { - id: String, - version: i32, - content: Vec, - #[serde(with = "date_format")] - created_time: DateTime, - #[serde(with = "date_format")] - last_edited_time: DateTime, + id: String, + version: i32, + content: Vec, + #[serde(with = "date_format")] + created_time: DateTime, + #[serde(with = "date_format")] + last_edited_time: DateTime, } #[derive(Serialize, Deserialize, Debug)] pub struct Page { - id: String, - version: i32, - properties: Properties, - #[serde(with = "date_format")] - created_time: DateTime, - #[serde(with = "date_format")] - last_edited_time: DateTime, - format: Format, + id: String, + version: i32, + properties: Properties, + #[serde(with = "date_format")] + created_time: DateTime, + #[serde(with = "date_format")] + last_edited_time: DateTime, + format: Format, } #[derive(Serialize, Deserialize, Debug)] pub struct Format { - page_icon: String, + page_icon: String, } impl Page { - fn to_loc(&self) -> String { - format!("\nhttps://MY_DOMAIN/{}\n\t{}\n\tdaily\n\t0.9\n\n", - self.id.replace('-', ""), - self.created_time.format("%Y-%m-%d"), - ) - } + fn to_loc(&self) -> String { + format!("\nhttps://MY_DOMAIN/{}\n\t{}\n\tdaily\n\t0.9\n\n", + self.id.replace('-', ""), + self.created_time.format("%Y-%m-%d"), + ) + } + fn to_entry(&self) -> String { + let mut entry = String::from("\n\t"); + let page_id = self.id.replace('-', ""); + entry.push_str(&format!( + "{}\n\t", + self.properties.title.get_title() + )); + entry.push_str(&format!( + "\n\t", + page_id + )); + entry.push_str(&format!("https://MY_DOMAIN/{}\n\t", page_id)); + entry.push_str(&format!( + "{}\n\t", + self.created_time.to_rfc3339() + )); + entry.push_str(&format!( + "{}\n\t", + self.last_edited_time.to_rfc3339() + )); + entry.push_str(&format!( + "{}\n\t", + self.properties.title.get_title() + )); + entry.push_str(&format!( + "{}\n", + self.properties.title.get_title() + )); + entry.push_str("\n"); + entry + } } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct Properties { - title: Title, + title: Title, } #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum Title { - String(String), - Array(Vec), + String(String), + Array(Vec<Title>), } impl Title { - fn get_title(&self) -> String { - match self { - Title::String(t) => t.to_string(), - Title::Array(ts) => { - return ts - .iter() - .map(|t| t.get_title()) - .collect::<Vec<String>>() - .join(""); - } - } + fn get_title(&self) -> String { + match self { + Title::String(t) => t.to_string(), + Title::Array(ts) => { + return ts + .iter() + .map(|t| t.get_title()) + .collect::<Vec<String>>() + .join(""); + } } + } } mod date_format { - use chrono::{DateTime, FixedOffset, NaiveDateTime, Utc}; - use serde::{self, Deserialize, Deserializer, Serializer}; + use chrono::{DateTime, FixedOffset, NaiveDateTime, Utc}; + use serde::{self, Deserialize, Deserializer, Serializer}; - const FORMAT: &str = "%Y-%m-%d"; + const FORMAT: &str = "%Y-%m-%d"; - pub fn serialize<S>(date: &DateTime<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let s = date.format(FORMAT).to_string(); - serializer.serialize_str(&s) - } + pub fn serialize<S>(date: &DateTime<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let s = date.format(FORMAT).to_string(); + serializer.serialize_str(&s) + } - pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error> - where - D: Deserializer<'de>, - { - let s = i64::deserialize(deserializer)?; - let tz_offset = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - match NaiveDateTime::from_timestamp_millis(s) { - Some(t) => Ok(DateTime::from_utc(t, tz_offset)), - None => Ok(Utc::now().with_timezone(&tz_offset)), - } + pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error> + where + D: Deserializer<'de>, + { + let s = i64::deserialize(deserializer)?; + let tz_offset = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + match NaiveDateTime::from_timestamp_millis(s) { + Some(t) => Ok(DateTime::from_utc(t, tz_offset)), + None => Ok(Utc::now().with_timezone(&tz_offset)), } + } } #[cfg(test)] mod tests { - use crate::page::QueryCollection; + use crate::page::QueryCollection; - #[test] - fn test_notion_json() { - let j = r#" + #[test] + fn test_notion_json() { + let j = r#" { "recordMap": { "__version__": 3, @@ -362,9 +423,9 @@ mod tests { } } }"#; - let p: QueryCollection = serde_json::from_str(j).unwrap(); - for (_key, block) in p.record_map.block { - println!("{}", block.value.get_page().unwrap_or_default()); - } + let p: QueryCollection = serde_json::from_str(j).unwrap(); + for (_key, block) in p.record_map.block { + println!("{}", block.value.get_loc().unwrap_or_default()); } + } }