support customized metadata

This commit is contained in:
Peter Cai 2020-04-08 17:39:44 +08:00
parent 4995de8372
commit 8fe31281f0
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
1 changed files with 80 additions and 7 deletions

View File

@ -72,6 +72,72 @@ async fn get_actions(_req: Request, url: Url) -> MyResult<Response> {
).internal_err()
}
#[derive(Deserialize)]
struct CustomMetadata {
url: Option<String>,
timestamp: Option<String> // Should be something `js_sys::Date::parse` could handle
}
struct Metadata {
url: String,
has_custom_url: bool,
timestamp: u64, // Seconds
has_custom_timestamp: bool
}
// You can customize metadata by adding something like
//
// ```json
// {
// "url": "xxx-xxx-xxx",
// "timestamp": "YYYY-mm-dd"
// }
// ```
//
// to the beginning of your article
// Normally when you update a post, the timestamp and URL will not be updated,
// but when you have custom metadata, they will always be updated.
// When the URL is updated, the old URL will automatically 301 to the new one
fn parse_custom_metadata_from_content(text: String) -> (Option<CustomMetadata>, String) {
if !text.starts_with("```json\n") {
(None, text)
} else {
match text.find("```\n\n") {
None => (None, text),
Some(pos) => {
let json = serde_json::from_str(&text[8..pos]).ok();
return (json, text[pos + 5..].to_owned())
}
}
}
}
// Generate metadata from uuid and title
// Fill in default value if custom value not present
fn build_metadata(custom: Option<CustomMetadata>, uuid: &str, title: &str) -> Metadata {
// Default values
let mut ret = Metadata {
url: title_to_url(&uuid, &title),
has_custom_url: false,
timestamp: Date::now() as u64 / 1000, // Seconds
has_custom_timestamp: false
};
if let Some(custom) = custom {
if let Some(url) = custom.url {
ret.url = url;
ret.has_custom_url = true;
}
if let Some(date) = custom.timestamp {
ret.timestamp = Date::parse(&date) as u64 / 1000; // Seconds
ret.has_custom_timestamp = true;
}
}
ret
}
async fn create_or_update_post(req: Request, url: Url) -> MyResult<Response> {
verify_secret!(url, params);
if req.method() != "POST" {
@ -88,27 +154,34 @@ async fn create_or_update_post(req: Request, url: Url) -> MyResult<Response> {
return Err(Error::BadRequest("At least one item must be supplied".into()));
}
// TODO: we should support customizing timestamp and URL from text
// and if there are option detected in text, it always overrides
// whatever was stored before
// If the URL is changed via this way, we should make sure the old
// URL actually 301's to the new URL
let uuid = data.items[0].uuid.clone();
let text = data.items[0].content.text.clone();
let title = data.items[0].content.title.clone();
let (custom_metadata, text) = parse_custom_metadata_from_content(text);
let metadata = build_metadata(custom_metadata, &uuid, &title);
let post = match blog::Post::find_by_uuid(&uuid).await {
Ok(mut post) => {
post.content = text;
post.title = title;
// Update metadata if custom ones are present
if metadata.has_custom_url {
post.url = metadata.url;
}
if metadata.has_custom_timestamp {
post.timestamp = metadata.timestamp;
}
post
},
Err(_) => {
blog::Post {
url: title_to_url(&uuid, &title),
url: metadata.url,
uuid: uuid,
title: title,
content: text,
timestamp: Date::now() as u64 / 1000 // Seconds
timestamp: metadata.timestamp
}
}
};