allow passing extra params to blog engine

This commit is contained in:
slonkazoid 2024-12-16 14:35:43 +03:00
parent e5cc685b0a
commit 9eddbdb881
Signed by: slonk
SSH key fingerprint: SHA256:tbZfJX4IOvZ0LGWOWu5Ijo8jfMPi78TU7x1VoEeCIjM
6 changed files with 82 additions and 18 deletions

20
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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";

View file

@ -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";

View file

@ -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)
}
}