This commit is contained in:
GitHub
2024-10-12 17:18:06 +08:00
parent d4f60791db
commit 1510fc1641
5 changed files with 80 additions and 175 deletions

100
Cargo.lock generated
View File

@@ -75,21 +75,6 @@ dependencies = [
"url",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arc-swap"
version = "1.7.1"
@@ -134,9 +119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a3a5ed3201df5658d1aa45060c5a57dc9dba8a8ada20d696d67cb0c479ee043"
dependencies = [
"chrono",
"derive_builder",
"diligent-date-parser",
"never",
"quick-xml",
]
@@ -478,10 +461,7 @@ version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"windows-targets",
]
[[package]]
@@ -503,12 +483,6 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.14"
@@ -628,37 +602,6 @@ dependencies = [
"serde",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -807,7 +750,6 @@ dependencies = [
"bincode 2.0.0-rc.3",
"cached",
"captcha",
"chrono",
"data-encoding",
"fast2s",
"garde",
@@ -1203,29 +1145,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1336,11 +1255,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jieba-rs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e2b0210dc78b49337af9e49d7ae41a39dceac6e5985613f1cf7763e2f76a25"
source = "git+https://github.com/messense/jieba-rs.git?rev=b39957e#b39957e9a6d738c4b00f3fa645da971ad426ec21"
dependencies = [
"cedarwood",
"derive_builder",
"fxhash",
"lazy_static",
"phf",
@@ -1636,12 +1553,6 @@ dependencies = [
"jobserver",
]
[[package]]
name = "never"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
@@ -3326,15 +3237,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-registry"
version = "0.2.0"

View File

@@ -6,7 +6,7 @@ license = "MIT License"
[dependencies]
ammonia = "4.0.0"
atom_syndication = "0.12"
atom_syndication = { version = "0.12", default-features = false }
axum = { version = "0.7.5", features = ["http1", "http2", "form", "query", "multipart", "tokio"], default-features = false }
axum-extra = { version = "0.9", features = ["typed-header"] }
axum_garde = { version = "0.20.0", default-features = false, features = ["form"] }
@@ -14,7 +14,6 @@ basic-toml = "*"
bincode = "2.0.0-rc.3"
cached = { version = "0.53", default-features = false, features = ["proc_macro", "ahash"] }
captcha = { version = "0.0", default-features = false }
chrono = { version = "0.4", default-features = false, features = ["clock"] }
data-encoding = "*"
fast2s = "0.3"
garde = { version = "0.20.0", features = ["derive"] }
@@ -23,7 +22,7 @@ identicon = { git = "https://github.com/freedit-dev/identicon.git", default-feat
image = { version = "0.25.2", default-features = false, features = ["jpeg", "png", "gif"] }
img-parts = "0.3.0"
indexmap = "2"
jieba-rs = "0.7.0"
jieba-rs = { git = "https://github.com/messense/jieba-rs.git", rev = "b39957e" }
jiff = { version = "0.1.13", default-features = false, features = ["std"] }
latex2mathml = "0.2.3"
mozjpeg = "0.10.10"

View File

@@ -26,10 +26,7 @@ use super::{
Claim, Comment, Feed, FormPost, Inn, InnType, Post, PostContent, PostStatus, SiteConfig, User,
};
use crate::{error::AppError, DB};
use atom_syndication::{
CategoryBuilder, ContentBuilder, EntryBuilder, FeedBuilder, LinkBuilder, PersonBuilder, Text,
WriteConfig,
};
use axum::{
extract::{Path, Query},
response::{IntoResponse, Redirect},
@@ -39,7 +36,6 @@ use axum_extra::{headers::Cookie, TypedHeader};
use axum_garde::WithValidation;
use bincode::config::standard;
use cached::proc_macro::cached;
use chrono::{DateTime, Utc};
use garde::Validate;
use jiff::Timestamp;
use rinja::filters::{escape, Html};
@@ -47,11 +43,7 @@ use rinja_axum::{into_response, Template};
use serde::Deserialize;
use sled::{transaction::ConflictableTransactionError, Transactional};
use sled::{Batch, Db};
use std::{
collections::{BTreeSet, HashMap, HashSet},
path::PathBuf,
sync::LazyLock,
};
use std::collections::{BTreeSet, HashMap, HashSet};
/// Page data: `inn_create.html`
#[derive(Template)]
@@ -1155,10 +1147,27 @@ fn recommend_users() -> Result<Vec<(u32, String)>, AppError> {
Ok(users)
}
static FEED_CONFIG: LazyLock<WriteConfig> = LazyLock::new(|| WriteConfig {
write_document_declaration: false,
indent_size: Some(2),
});
/// Page data: `atom.xml`
#[derive(Template)]
#[template(path = "atom.xml")]
struct PageAtom {
domain: String,
title: String,
iid: u32,
categories: Vec<String>,
updated: String,
subtitle: String,
entries: Vec<Entry>,
}
struct Entry {
title: String,
iid: u32,
pid: u32,
updated: String,
author: (String, u32),
content: String,
}
/// `GET /inn/:iid/atom.xml` inn page
pub(crate) async fn inn_feed(Path(i): Path<String>) -> Result<impl IntoResponse, AppError> {
@@ -1177,18 +1186,12 @@ pub(crate) async fn inn_feed(Path(i): Path<String>) -> Result<impl IntoResponse,
let mut index = Vec::with_capacity(page_params.n);
let title;
let description;
let alternate_link = PathBuf::from(&site_config.domain)
.join("inn")
.join(iid.to_string())
.display()
.to_string();
let self_link = format!("{alternate_link}/atom.xml");
let mut categories = Vec::new();
if iid == 0 {
index = get_pids_all(&DB, &[], &page_params, false)?;
title = site_config.site_name;
description = site_config.description;
description = md2html(&site_config.description);
} else {
let inn: Inn = get_one(&DB, "inns", iid)?;
description = md2html(&inn.about);
@@ -1196,8 +1199,8 @@ pub(crate) async fn inn_feed(Path(i): Path<String>) -> Result<impl IntoResponse,
index = get_pids_by_iids(&DB, &[iid], &page_params)?;
}
title = inn.inn_name;
for i in &inn.topics {
categories.push(CategoryBuilder::default().term(i).build());
for i in inn.topics {
categories.push(i);
}
}
@@ -1206,58 +1209,34 @@ pub(crate) async fn inn_feed(Path(i): Path<String>) -> Result<impl IntoResponse,
let post: Post = get_one(&DB, "posts", i)?;
let user: User = get_one(&DB, "users", post.uid)?;
let content = post.content.to_html(&DB)?;
let post_link = format!("/post/{}/{}", post.iid, post.pid);
let dt: DateTime<Utc> = DateTime::<Utc>::from_timestamp(post.created_at, 0).unwrap();
let entry = EntryBuilder::default()
.authors(vec![PersonBuilder::default()
.name(user.username)
.uri(Some(format!("{}/user/{}", &site_config.domain, user.uid)))
.build()])
.id(&post_link)
.updated(dt)
.links(vec![LinkBuilder::default()
.rel("alternate".to_owned())
.mime_type(Some("text/html".to_owned()))
.href(post_link)
.build()])
.title(Text::plain(post.title))
.content(Some(ContentBuilder::default().value(Some(content)).build()))
.build();
let updated = Timestamp::from_second(post.created_at)
.unwrap()
.strftime("%Y-%m-%dT%H:%M:%SZ")
.to_string();
let entry = Entry {
title: post.title,
iid: post.iid,
pid: post.pid,
updated,
author: (user.username, user.uid),
content,
};
entries.push(entry);
}
let mut feed = FeedBuilder::default()
.title(title)
.subtitle(Some(Text::plain(description)))
.id(&self_link)
.link(
LinkBuilder::default()
.rel("self".to_owned())
.mime_type(Some("application/atom+xml".to_owned()))
.href(self_link)
.build(),
)
.link(
LinkBuilder::default()
.rel("alternate".to_owned())
.mime_type(Some("text/html".to_owned()))
.href(alternate_link)
.build(),
)
.build();
let updated = entries[0].updated.clone();
let page_atom = PageAtom {
domain: site_config.domain,
title,
iid,
categories,
updated,
subtitle: description,
entries,
};
feed.set_categories(categories);
feed.set_entries(entries);
let mut output = Vec::new();
feed.write_with_config(&mut output, *FEED_CONFIG).unwrap();
let headers = [(
http::header::CONTENT_TYPE,
http::HeaderValue::from_static("application/xml"),
)];
Ok((headers, output))
Ok(into_response(&page_atom))
}
/// get [OutPostList] from pids

View File

@@ -3,10 +3,10 @@
#![warn(clippy::pedantic)]
// #![warn(clippy::unwrap_used)]
use chrono::Utc;
use freedit::{
router, AppError, CONFIG, DB, VERSION, {clear_invalid, cron_feed, Tan},
};
use jiff::Timestamp;
use std::{fs, net::SocketAddr, path::PathBuf};
use tokio::net::TcpListener;
use tracing::{error, info, warn};
@@ -89,7 +89,7 @@ fn create_snapshot(db: &sled::Db) {
let checksum = db.checksum().unwrap();
info!(%checksum);
let ts = Utc::now().format("%Y-%m-%d-%H-%M-%S");
let ts = Timestamp::now().strftime("%Y-%m-%d-%H-%M-%S");
let mut snapshot_path = PathBuf::from("snapshots");
if !snapshot_path.exists() {
fs::create_dir_all(&snapshot_path).unwrap();

25
templates/atom.xml Normal file
View File

@@ -0,0 +1,25 @@
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ title }}</title>
<id>{{ domain }}/inn/{{ iid }}</id>
<updated>{{ updated }}</updated>
<link href="{{ domain }}/inn/{{ iid }}/atom.xml" rel="self" type="application/atom+xml"/>
<link href="{{ domain }}/inn/{{ iid }}/" rel="alternate" type="text/html"/>
<subtitle>{{ subtitle }}</subtitle>
{% for category in categories %}
<category term="{{ category }}" />
{% endfor %}
{% for entry in entries %}
<entry>
<title>{{ entry.title }}</title>
<id>{{ domain }}/inn/{{ entry.iid }}/{{ entry.pid }}</id>
<updated>{{ entry.updated }}</updated>
<author>
<name>{{ entry.author.0 }}</name>
<uri>{{ domain }}/user/{{ entry.author.1 }}</uri>
</author>
<link href="{{ domain }}/post/{{entry.iid}}/{{entry.pid}}" rel="alternate" type="text/html"/>
<content>{{ entry.content|trim }}</content>
</entry>
{% endfor %}
</feed>