allow passing extra params to blog engine
This commit is contained in:
parent
e5cc685b0a
commit
9eddbdb881
6 changed files with 82 additions and 18 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -318,6 +318,7 @@ dependencies = [
|
|||
"rss",
|
||||
"scc",
|
||||
"serde",
|
||||
"serde-value",
|
||||
"serde_json",
|
||||
"syntect",
|
||||
"thiserror",
|
||||
|
@ -1541,6 +1542,15 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -1930,6 +1940,16 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-value"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||
dependencies = [
|
||||
"ordered-float",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.206"
|
||||
|
|
|
@ -47,6 +47,7 @@ notify-debouncer-full = { version = "0.3.1", default-features = false }
|
|||
rss = "2.0.7"
|
||||
scc = { version = "2.1.0", features = ["serde"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde-value = "0.7.0"
|
||||
serde_json = { version = "1.0.124", features = ["preserve_order"] }
|
||||
syntect = "5.2.0"
|
||||
thiserror = "1.0.58"
|
||||
|
|
21
src/app.rs
21
src/app.rs
|
@ -13,6 +13,7 @@ use include_dir::{include_dir, Dir};
|
|||
use rss::{Category, ChannelBuilder, ItemBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Map;
|
||||
use serde_value::Value;
|
||||
use tokio::sync::RwLock;
|
||||
use tower::service_fn;
|
||||
use tower_http::services::ServeDir;
|
||||
|
@ -78,6 +79,8 @@ struct QueryParams {
|
|||
tag: Option<String>,
|
||||
#[serde(rename = "n")]
|
||||
num_posts: Option<usize>,
|
||||
#[serde(flatten)]
|
||||
other: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
fn collect_tags(posts: &Vec<PostMetadata>) -> Map<String, serde_json::Value> {
|
||||
|
@ -130,7 +133,11 @@ async fn index(
|
|||
Query(query): Query<QueryParams>,
|
||||
) -> AppResult<impl IntoResponse> {
|
||||
let posts = posts
|
||||
.get_max_n_post_metadata_with_optional_tag_sorted(query.num_posts, query.tag.as_deref())
|
||||
.get_max_n_post_metadata_with_optional_tag_sorted(
|
||||
query.num_posts,
|
||||
query.tag.as_deref(),
|
||||
&query.other,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let tags = collect_tags(&posts);
|
||||
|
@ -160,7 +167,11 @@ async fn all_posts(
|
|||
Query(query): Query<QueryParams>,
|
||||
) -> AppResult<Json<Vec<PostMetadata>>> {
|
||||
let posts = posts
|
||||
.get_max_n_post_metadata_with_optional_tag_sorted(query.num_posts, query.tag.as_deref())
|
||||
.get_max_n_post_metadata_with_optional_tag_sorted(
|
||||
query.num_posts,
|
||||
query.tag.as_deref(),
|
||||
&query.other,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Json(posts))
|
||||
|
@ -181,6 +192,7 @@ async fn rss(
|
|||
.as_ref()
|
||||
.and(Some(Filter::Tags(query.tag.as_deref().as_slice())))
|
||||
.as_slice(),
|
||||
&query.other,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
@ -234,8 +246,9 @@ async fn post(
|
|||
..
|
||||
}): State<AppState>,
|
||||
Path(name): Path<String>,
|
||||
Query(query): Query<QueryParams>,
|
||||
) -> AppResult<impl IntoResponse> {
|
||||
match posts.get_post(&name).await? {
|
||||
match posts.get_post(&name, &query.other).await? {
|
||||
ReturnedPost::Rendered(ref meta, rendered, rendered_in) => {
|
||||
let joined_tags = meta.tags.join(", ");
|
||||
|
||||
|
@ -256,7 +269,7 @@ async fn post(
|
|||
joined_tags,
|
||||
style: &config.style,
|
||||
raw_name: if config.markdown_access {
|
||||
raw_name = posts.get_raw(&meta.name).await?;
|
||||
raw_name = posts.as_raw(&meta.name).await?;
|
||||
raw_name.as_deref()
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
|
@ -6,6 +7,7 @@ use axum::async_trait;
|
|||
use axum::http::HeaderValue;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use serde_value::Value;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
|
||||
use tokio::time::Instant;
|
||||
|
@ -32,6 +34,7 @@ impl PostManager for Blag {
|
|||
async fn get_all_posts(
|
||||
&self,
|
||||
filters: &[Filter<'_>],
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<(PostMetadata, String, RenderStats)>, PostError> {
|
||||
let mut set = FuturesUnordered::new();
|
||||
let mut meow = Vec::new();
|
||||
|
@ -57,7 +60,9 @@ impl PostManager for Blag {
|
|||
};
|
||||
|
||||
if name.ends_with(".sh") {
|
||||
set.push(async move { self.get_post(name.trim_end_matches(".sh")).await });
|
||||
set.push(
|
||||
async move { self.get_post(name.trim_end_matches(".sh"), query).await },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +89,11 @@ impl PostManager for Blag {
|
|||
Ok(meow)
|
||||
}
|
||||
|
||||
async fn get_post(&self, name: &str) -> Result<ReturnedPost, PostError> {
|
||||
async fn get_post(
|
||||
&self,
|
||||
name: &str,
|
||||
_query: &HashMap<String, Value>,
|
||||
) -> Result<ReturnedPost, PostError> {
|
||||
let mut path = self.root.join(name);
|
||||
|
||||
if name.ends_with(".sh") {
|
||||
|
@ -157,7 +166,7 @@ impl PostManager for Blag {
|
|||
))
|
||||
}
|
||||
|
||||
async fn get_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
async fn as_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
let mut buf = String::with_capacity(name.len() + 3);
|
||||
buf += name;
|
||||
buf += ".sh";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
@ -14,6 +14,7 @@ use color_eyre::eyre::{self, Context};
|
|||
use comrak::plugins::syntect::SyntectAdapter;
|
||||
use fronma::parser::{parse, ParsedData};
|
||||
use serde::Deserialize;
|
||||
use serde_value::Value;
|
||||
use tokio::fs;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tracing::warn;
|
||||
|
@ -140,6 +141,7 @@ impl PostManager for MarkdownPosts {
|
|||
async fn get_all_posts(
|
||||
&self,
|
||||
filters: &[Filter<'_>],
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<(PostMetadata, String, RenderStats)>, PostError> {
|
||||
let mut posts = Vec::new();
|
||||
|
||||
|
@ -156,7 +158,7 @@ impl PostManager for MarkdownPosts {
|
|||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let post = self.get_post(&name).await?;
|
||||
let post = self.get_post(&name, query).await?;
|
||||
if let ReturnedPost::Rendered(meta, content, stats) = post
|
||||
&& meta.apply_filters(filters)
|
||||
{
|
||||
|
@ -171,6 +173,7 @@ impl PostManager for MarkdownPosts {
|
|||
async fn get_all_post_metadata(
|
||||
&self,
|
||||
filters: &[Filter<'_>],
|
||||
_query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<PostMetadata>, PostError> {
|
||||
let mut posts = Vec::new();
|
||||
|
||||
|
@ -211,7 +214,11 @@ impl PostManager for MarkdownPosts {
|
|||
Ok(posts)
|
||||
}
|
||||
|
||||
async fn get_post(&self, name: &str) -> Result<ReturnedPost, PostError> {
|
||||
async fn get_post(
|
||||
&self,
|
||||
name: &str,
|
||||
_query: &HashMap<String, Value>,
|
||||
) -> Result<ReturnedPost, PostError> {
|
||||
if self.config.markdown_access && name.ends_with(".md") {
|
||||
let path = self.config.dirs.posts.join(name);
|
||||
|
||||
|
@ -287,7 +294,7 @@ impl PostManager for MarkdownPosts {
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
async fn as_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
let mut buf = String::with_capacity(name.len() + 3);
|
||||
buf += name;
|
||||
buf += ".md";
|
||||
|
|
|
@ -2,11 +2,12 @@ pub mod blag;
|
|||
pub mod cache;
|
||||
pub mod markdown_posts;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
|
||||
use axum::{async_trait, http::HeaderValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_value::Value;
|
||||
|
||||
use crate::error::PostError;
|
||||
pub use blag::Blag;
|
||||
|
@ -74,8 +75,9 @@ pub trait PostManager {
|
|||
async fn get_all_post_metadata(
|
||||
&self,
|
||||
filters: &[Filter<'_>],
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<PostMetadata>, PostError> {
|
||||
self.get_all_posts(filters)
|
||||
self.get_all_posts(filters, query)
|
||||
.await
|
||||
.map(|vec| vec.into_iter().map(|(meta, ..)| meta).collect())
|
||||
}
|
||||
|
@ -83,15 +85,19 @@ pub trait PostManager {
|
|||
async fn get_all_posts(
|
||||
&self,
|
||||
filters: &[Filter<'_>],
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<(PostMetadata, String, RenderStats)>, PostError>;
|
||||
|
||||
async fn get_max_n_post_metadata_with_optional_tag_sorted(
|
||||
&self,
|
||||
n: Option<usize>,
|
||||
tag: Option<&str>,
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<Vec<PostMetadata>, PostError> {
|
||||
let filters = tag.and(Some(Filter::Tags(tag.as_slice())));
|
||||
let mut posts = self.get_all_post_metadata(filters.as_slice()).await?;
|
||||
let mut posts = self
|
||||
.get_all_post_metadata(filters.as_slice(), query)
|
||||
.await?;
|
||||
// we still want some semblance of order if created_at is None so sort by mtime as well
|
||||
posts.sort_unstable_by_key(|metadata| metadata.modified_at.unwrap_or_default());
|
||||
posts.sort_by_key(|metadata| metadata.created_at.unwrap_or_default());
|
||||
|
@ -104,19 +110,27 @@ pub trait PostManager {
|
|||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn get_post_metadata(&self, name: &str) -> Result<PostMetadata, PostError> {
|
||||
match self.get_post(name).await? {
|
||||
async fn get_post_metadata(
|
||||
&self,
|
||||
name: &str,
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<PostMetadata, PostError> {
|
||||
match self.get_post(name, query).await? {
|
||||
ReturnedPost::Rendered(metadata, ..) => Ok(metadata),
|
||||
ReturnedPost::Raw(..) => Err(PostError::NotFound(name.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_post(&self, name: &str) -> Result<ReturnedPost, PostError>;
|
||||
async fn get_post(
|
||||
&self,
|
||||
name: &str,
|
||||
query: &HashMap<String, Value>,
|
||||
) -> Result<ReturnedPost, PostError>;
|
||||
|
||||
async fn cleanup(&self) {}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn get_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
async fn as_raw(&self, name: &str) -> Result<Option<String>, PostError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue