Compare commits

..

2 Commits

Author SHA1 Message Date
2e47c91985 404 page
Some checks failed
test / cargo test (push) Failing after 1m24s
2024-10-07 10:14:32 +02:00
08050baf98 broadcasts segments enum 2024-10-07 10:08:56 +02:00
10 changed files with 84 additions and 62 deletions

View File

@ -24,7 +24,7 @@
@404 { @404 {
expression {http.error.status_code} == 404 expression {http.error.status_code} == 404
} }
rewrite @404 /404.html rewrite @404 /not-found.html
file_server file_server
} }
} }

View File

@ -52,6 +52,7 @@ clean:
# SSG # SSG
ssg: ssg:
- wget --no-convert-links -r -p -E -P dist --no-host-directories 127.0.0.1:{{port}} - wget --no-convert-links -r -p -E -P dist --no-host-directories 127.0.0.1:{{port}}
- wget --no-convert-links -p -E -P dist --no-host-directories 127.0.0.1:{{port}}/not-found
find generated_images/ -name "*_og*" -exec cp --parents {} dist/ \; find generated_images/ -name "*_og*" -exec cp --parents {} dist/ \;
# Preview server # Preview server

View File

@ -1,14 +1,23 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::Deserialize; use serde::{Deserialize, Serialize};
use crate::post_utils::post_parser::deserialize_date; use crate::post_utils::post_parser::deserialize_date;
pub const BLOG_POST_PATH: &str = "_posts/blog"; pub const BLOG_POST_PATH: &str = "_posts/blog";
#[derive(Deserialize, Debug, Clone)] #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")] // Optional, this converts enum variants to lowercase
pub enum Segment {
Blog,
Broadcasts,
Featured,
Cookbook,
}
#[derive(Deserialize, Debug)]
pub struct BlogPostMetadata { pub struct BlogPostMetadata {
pub title: String, pub title: String,
pub segments: Vec<String>, pub segments: Vec<Segment>,
pub published: bool, pub published: bool,
#[serde(deserialize_with = "deserialize_date")] #[serde(deserialize_with = "deserialize_date")]
pub date: DateTime<Utc>, pub date: DateTime<Utc>,

View File

@ -1,5 +1,4 @@
use askama_axum::IntoResponse; use axum::{self};
use axum::{self, extract::OriginalUri, http::StatusCode};
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use tower_livereload::LiveReloadLayer; use tower_livereload::LiveReloadLayer;
use tracing::info; use tracing::info;
@ -57,3 +56,5 @@ async fn main() {
// THINK deploy to alula? rather then katelyn? can be change whenever // THINK deploy to alula? rather then katelyn? can be change whenever
// //
// TODO 404 page // TODO 404 page
// TODO view page transitions
// TODO cookbook

View File

@ -6,10 +6,11 @@ use tokio::try_join;
use tracing::debug; use tracing::debug;
use crate::{ use crate::{
blog_posts::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH}, blog_posts::blog_post_model::{BlogPostMetadata, Segment, BLOG_POST_PATH},
components::site_header::{HeaderProps, Link}, components::site_header::{HeaderProps, Link},
post_utils::{ post_utils::{
post_listing::get_post_list, post_listing::get_post_list,
segments::get_posts_by_segment,
tags::{get_popular_tags, get_posts_by_tag}, tags::{get_popular_tags, get_posts_by_tag},
}, },
projects::featured_projects::get_featured_projects, projects::featured_projects::get_featured_projects,
@ -17,6 +18,7 @@ use crate::{
use super::post_list::PostListTemplate; use super::post_list::PostListTemplate;
// TODO Refactor to render post list for the same broadcasts, blog and cookbook
pub async fn render_blog_post_list( pub async fn render_blog_post_list(
tag: Option<Path<String>>, tag: Option<Path<String>>,
OriginalUri(original_uri): OriginalUri, OriginalUri(original_uri): OriginalUri,
@ -24,18 +26,14 @@ pub async fn render_blog_post_list(
// I will forget what happens here in a week. But essentially it's pattern matching and shadowing // I will forget what happens here in a week. But essentially it's pattern matching and shadowing
let tag = tag.map(|Path(tag)| tag); let tag = tag.map(|Path(tag)| tag);
let (blog_tags, featured_projects, mut post_list) = try_join!( let (blog_tags, featured_projects, post_list) = try_join!(
get_popular_tags(Some("blog".to_string())), get_popular_tags(Some(Segment::Blog)),
get_featured_projects(), get_featured_projects(),
get_post_list::<BlogPostMetadata>(BLOG_POST_PATH) get_post_list::<BlogPostMetadata>(BLOG_POST_PATH)
)?; )?;
post_list.sort_by_key(|post| post.metadata.date); let posts = get_posts_by_segment(post_list, &[Segment::Blog]);
post_list.retain(|post| post.metadata.published); let posts = get_posts_by_tag(posts, &tag);
post_list.retain(|post| post.metadata.segments.contains(&"blog".to_string()));
post_list.reverse();
let posts = get_posts_by_tag(post_list, &tag);
let header_props = match tag { let header_props = match tag {
Some(_) => HeaderProps::with_back_link(Link { Some(_) => HeaderProps::with_back_link(Link {
href: "/blog".to_string(), href: "/blog".to_string(),
@ -55,7 +53,7 @@ pub async fn render_blog_post_list(
Ok(PostListTemplate { Ok(PostListTemplate {
title, title,
og_title, og_title,
segment: "blog".to_string(), segment: Segment::Blog,
posts, posts,
header_props, header_props,
tags: blog_tags, tags: blog_tags,

View File

@ -6,10 +6,11 @@ use tokio::try_join;
use tracing::debug; use tracing::debug;
use crate::{ use crate::{
blog_posts::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH}, blog_posts::blog_post_model::{BlogPostMetadata, Segment, BLOG_POST_PATH},
components::site_header::{HeaderProps, Link}, components::site_header::{HeaderProps, Link},
post_utils::{ post_utils::{
post_listing::get_post_list, post_listing::get_post_list,
segments::get_posts_by_segment,
tags::{get_popular_tags, get_posts_by_tag}, tags::{get_popular_tags, get_posts_by_tag},
}, },
projects::featured_projects::get_featured_projects, projects::featured_projects::get_featured_projects,
@ -24,18 +25,14 @@ pub async fn render_broadcast_post_list(
// I will forget what happens here in a week. But essentially it's pattern matching and shadowing // I will forget what happens here in a week. But essentially it's pattern matching and shadowing
let tag = tag.map(|Path(tag)| tag); let tag = tag.map(|Path(tag)| tag);
let (popular_tags, featured_projects, mut post_list) = try_join!( let (popular_tags, featured_projects, post_list) = try_join!(
get_popular_tags(Some("broadcasts".to_string())), get_popular_tags(Some(Segment::Broadcasts)),
get_featured_projects(), get_featured_projects(),
get_post_list::<BlogPostMetadata>(BLOG_POST_PATH) get_post_list::<BlogPostMetadata>(BLOG_POST_PATH)
)?; )?;
post_list.sort_by_key(|post| post.metadata.date); let posts = get_posts_by_segment(post_list, &[Segment::Broadcasts]);
post_list.retain(|post| post.metadata.published); let posts = get_posts_by_tag(posts, &tag);
post_list.retain(|post| post.metadata.segments.contains(&"broadcasts".to_string()));
post_list.reverse();
let posts = get_posts_by_tag(post_list, &tag);
let header_props = match tag { let header_props = match tag {
Some(_) => HeaderProps::with_back_link(Link { Some(_) => HeaderProps::with_back_link(Link {
@ -56,7 +53,7 @@ pub async fn render_broadcast_post_list(
Ok(PostListTemplate { Ok(PostListTemplate {
title: title.clone(), title: title.clone(),
og_title: title, og_title: title,
segment: "broadcasts".to_string(), segment: Segment::Broadcasts,
posts, posts,
header_props, header_props,
tags: popular_tags, tags: popular_tags,

View File

@ -5,7 +5,7 @@ use axum::http::StatusCode;
use tokio::try_join; use tokio::try_join;
use crate::{ use crate::{
blog_posts::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH}, blog_posts::blog_post_model::{BlogPostMetadata, Segment, BLOG_POST_PATH},
components::site_header::HeaderProps, components::site_header::HeaderProps,
filters, filters,
post_utils::{ post_utils::{
@ -28,8 +28,8 @@ pub struct IndexTemplate {
pub async fn render_index() -> Result<IndexTemplate, StatusCode> { pub async fn render_index() -> Result<IndexTemplate, StatusCode> {
let (blog_tags, broadcasts_tags, all_posts, featured_projects) = try_join!( let (blog_tags, broadcasts_tags, all_posts, featured_projects) = try_join!(
get_popular_tags(Some("blog".to_string())), get_popular_tags(Some(Segment::Blog)),
get_popular_tags(Some("broadcasts".to_string())), get_popular_tags(Some(Segment::Broadcasts)),
get_post_list::<BlogPostMetadata>(BLOG_POST_PATH), get_post_list::<BlogPostMetadata>(BLOG_POST_PATH),
get_featured_projects() get_featured_projects()
)?; )?;
@ -39,12 +39,10 @@ pub async fn render_index() -> Result<IndexTemplate, StatusCode> {
all_posts.into_iter().map(Rc::new).collect(); all_posts.into_iter().map(Rc::new).collect();
let featured_blog_posts = let featured_blog_posts =
ref_get_posts_by_segment(&all_posts_rc, &["blog".to_string(), "featured".to_string()]); ref_get_posts_by_segment(&all_posts_rc, &[Segment::Blog, Segment::Featured]);
let featured_broadcasts = ref_get_posts_by_segment( let featured_broadcasts =
&all_posts_rc, ref_get_posts_by_segment(&all_posts_rc, &[Segment::Broadcasts, Segment::Featured]);
&["broadcasts".to_string(), "featured".to_string()],
);
Ok(IndexTemplate { Ok(IndexTemplate {
header_props: HeaderProps::default(), header_props: HeaderProps::default(),

View File

@ -1,8 +1,11 @@
use askama::Template; use askama::Template;
use crate::{ use crate::{
blog_posts::blog_post_model::BlogPostMetadata, components::site_header::HeaderProps, filters, blog_posts::blog_post_model::{BlogPostMetadata, Segment},
post_utils::post_parser::ParseResult, projects::project_model::ProjectMetadata, components::site_header::HeaderProps,
filters,
post_utils::post_parser::ParseResult,
projects::project_model::ProjectMetadata,
}; };
#[derive(Template)] #[derive(Template)]
@ -10,7 +13,7 @@ use crate::{
pub struct PostListTemplate { pub struct PostListTemplate {
pub title: String, pub title: String,
pub og_title: String, pub og_title: String,
pub segment: String, pub segment: Segment,
pub posts: Vec<ParseResult<BlogPostMetadata>>, pub posts: Vec<ParseResult<BlogPostMetadata>>,
pub header_props: HeaderProps, pub header_props: HeaderProps,
pub tags: Vec<String>, pub tags: Vec<String>,

View File

@ -1,35 +1,50 @@
use std::rc::Rc; use std::{fmt::Display, rc::Rc};
use crate::blog_posts::blog_post_model::BlogPostMetadata; use crate::blog_posts::blog_post_model::{BlogPostMetadata, Segment};
use super::post_parser::ParseResult; use super::post_parser::ParseResult;
// // TODO convert segmetns to enum, find out how to serde to enum vlaue impl Segment {
// pub fn get_posts_by_segment( fn as_str(&self) -> &'static str {
// post_list: &Vec<ParseResult<BlogPostMetadata>>, match self {
// segments: &Vec<String>, Segment::Blog => "blog",
// ) -> Vec<ParseResult<BlogPostMetadata>> { Segment::Broadcasts => "broadcasts",
// let mut filtered_posts: Vec<ParseResult<BlogPostMetadata>> = post_list Segment::Featured => "featured",
// .iter() Segment::Cookbook => "cookbook",
// .filter(|post| { }
// segments }
// .iter() }
// .all(|segment| post.metadata.segments.contains(segment))
// }) // Filter by segments
// .filter(|post| post.metadata.published) // Filter only published posts
// .cloned()
// .collect();
// // Sort by date in descending order impl Display for Segment {
// filtered_posts.sort_by_key(|post| post.metadata.date); fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// filtered_posts.reverse(); write!(f, "{}", self.as_str())
}
}
// filtered_posts pub fn get_posts_by_segment(
// } post_list: Vec<ParseResult<BlogPostMetadata>>,
segments: &[Segment],
) -> Vec<ParseResult<BlogPostMetadata>> {
let mut filtered_posts: Vec<ParseResult<BlogPostMetadata>> = post_list
.into_iter()
.filter(|post| {
segments
.iter()
.all(|segment| post.metadata.segments.contains(segment))
}) // Filter by segments
.filter(|post| post.metadata.published) // Filter only published posts
.collect();
// Sort by date in descending order
filtered_posts.sort_by_key(|post| post.metadata.date);
filtered_posts.reverse();
filtered_posts
}
pub fn ref_get_posts_by_segment( pub fn ref_get_posts_by_segment(
post_list: &[Rc<ParseResult<BlogPostMetadata>>], post_list: &[Rc<ParseResult<BlogPostMetadata>>],
segments: &[String], segments: &[Segment],
) -> Vec<Rc<ParseResult<BlogPostMetadata>>> { ) -> Vec<Rc<ParseResult<BlogPostMetadata>>> {
let mut filtered_posts: Vec<Rc<ParseResult<BlogPostMetadata>>> = post_list let mut filtered_posts: Vec<Rc<ParseResult<BlogPostMetadata>>> = post_list
.iter() // Use iter() to borrow instead of consuming the original vector .iter() // Use iter() to borrow instead of consuming the original vector

View File

@ -2,11 +2,11 @@ use axum::http::StatusCode;
use std::collections::HashMap; use std::collections::HashMap;
use tracing::debug; use tracing::debug;
use crate::blog_posts::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH}; use crate::blog_posts::blog_post_model::{BlogPostMetadata, Segment, BLOG_POST_PATH};
use super::{post_listing::get_post_list, post_parser::ParseResult}; use super::{post_listing::get_post_list, post_parser::ParseResult};
pub async fn get_popular_tags(segment: Option<String>) -> Result<Vec<String>, StatusCode> { pub async fn get_popular_tags(segment: Option<Segment>) -> Result<Vec<String>, StatusCode> {
const TAGS_LENGTH: usize = 7; const TAGS_LENGTH: usize = 7;
let mut post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?; let mut post_list = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;