Compare commits

...

9 commits

Author SHA1 Message Date
6f7b9b7350
remove unused templates 2024-07-01 03:24:11 +03:00
a8a1dca444
add alt text 2024-07-01 03:21:33 +03:00
cee11ba07a
add TODO 2024-07-01 03:17:21 +03:00
38d93a66ba
add color to index 2024-07-01 03:16:17 +03:00
3623b61fbe
optimization 2024-07-01 03:14:26 +03:00
bd093e7c20
add default color 2024-07-01 02:53:04 +03:00
658ddaf820
document options 2024-07-01 02:44:43 +03:00
342a353b36
better post icon and color support 2024-07-01 02:34:40 +03:00
602f57581a
remove shortcut icon 2024-07-01 02:24:32 +03:00
9 changed files with 76 additions and 35 deletions

View file

@ -1,7 +1,7 @@
--- ---
title: "README" title: README
description: "the README.md file of this project" description: the README.md file of this project
author: "slonkazoid" author: slonkazoid
created_at: 2024-04-18T04:15:26+03:00 created_at: 2024-04-18T04:15:26+03:00
--- ---
@ -14,6 +14,8 @@ blazingly fast markdown blog software written in rust memory safe
- [x] RSS - [x] RSS
- [x] finish writing this document - [x] finish writing this document
- [x] document config - [x] document config
- [ ] blog thumbnail and favicon
- [x] alt text for post icon
- [ ] extend syntect options - [ ] extend syntect options
- [ ] general cleanup of code - [ ] general cleanup of code
- [ ] better error reporting and error pages - [ ] better error reporting and error pages
@ -46,6 +48,7 @@ date_format = "RFC3339" # format string used to format dates in the backend
# so the date can be formatted by the browser. # so the date can be formatted by the browser.
# format: https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers # format: https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers
js_enable = true # enable javascript (required for above) js_enable = true # enable javascript (required for above)
default_color = "#f5c2e7" # default embed color, optional
[rss] [rss]
enable = false # serve an rss field under /feed.xml enable = false # serve an rss field under /feed.xml
@ -55,6 +58,7 @@ link = "https://..." # public url of the blog, required if rss is enabled
[dirs] [dirs]
posts = "posts" # where posts are stored posts = "posts" # where posts are stored
media = "media" # directory served under /media/ media = "media" # directory served under /media/
static = "static" # directory server under /static/ (css and js)
[http] [http]
host = "0.0.0.0" # ip to listen on host = "0.0.0.0" # ip to listen on
@ -125,15 +129,22 @@ every post **must** begin with a **valid** front matter. else it wont be listed
in / & /posts, and when you navigate to it, you will be met with an error page. in / & /posts, and when you navigate to it, you will be met with an error page.
the error page will tell you what the problem is. the error page will tell you what the problem is.
example: full example:
```md ```md
--- ---
title: "README" title: My first post # title of the post
description: "the README.md file of this project" description: The first post on this awesome blog! # short description of the post
author: "slonkazoid" author: Blubber256 # author of the post
created_at: 2024-04-18T04:15:26+03:00 icon: /media/first-post/icon.png # icon/thumbnail of post used in embeds
#modified_at: ... # see above icon_alt: Picture of a computer running DOOM
color: "#00aacc" # color of post, also used in embeds
created_at: 2024-04-18T04:15:26+03:00 # date of writing, this is highly
# recommended if you are on a system which doesnt have btime (like musl),
# because this is fetched from file stats by default
#modified_at: ... # see above. this is also fetched from the filesystem
tags: # tags, or keywords, used in meta and also in the ui
- lifestyle
--- ---
``` ```

View file

@ -27,24 +27,26 @@ pub struct AppState {
#[derive(Template)] #[derive(Template)]
#[template(path = "index.html")] #[template(path = "index.html")]
struct IndexTemplate { struct IndexTemplate<'a> {
title: String, title: &'a str,
description: String, description: &'a str,
posts: Vec<PostMetadata>, posts: Vec<PostMetadata>,
rss: bool, rss: bool,
df: DateFormat, df: &'a DateFormat,
js: bool, js: bool,
color: Option<&'a str>,
} }
#[derive(Template)] #[derive(Template)]
#[template(path = "post.html")] #[template(path = "post.html")]
struct PostTemplate { struct PostTemplate<'a> {
meta: PostMetadata, meta: &'a PostMetadata,
rendered: String, rendered: String,
rendered_in: RenderStats, rendered_in: RenderStats,
markdown_access: bool, markdown_access: bool,
df: DateFormat, df: &'a DateFormat,
js: bool, js: bool,
color: Option<&'a str>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -54,22 +56,24 @@ struct QueryParams {
num_posts: Option<usize>, num_posts: Option<usize>,
} }
async fn index( async fn index<'a>(
State(AppState { config, posts }): State<AppState>, State(AppState { config, posts }): State<AppState>,
Query(query): Query<QueryParams>, Query(query): Query<QueryParams>,
) -> AppResult<IndexTemplate> { ) -> AppResult<Response> {
let posts = posts let posts = posts
.get_max_n_post_metadata_with_optional_tag_sorted(query.num_posts, query.tag.as_ref()) .get_max_n_post_metadata_with_optional_tag_sorted(query.num_posts, query.tag.as_ref())
.await?; .await?;
Ok(IndexTemplate { Ok(IndexTemplate {
title: config.title.clone(), title: &config.title,
description: config.description.clone(), description: &config.description,
posts, posts,
rss: config.rss.enable, rss: config.rss.enable,
df: config.date_format.clone(), df: &config.date_format,
js: config.js_enable, js: config.js_enable,
}) color: config.default_color.as_deref(),
}
.into_response())
} }
async fn all_posts( async fn all_posts(
@ -147,18 +151,16 @@ async fn post(
Path(name): Path<String>, Path(name): Path<String>,
) -> AppResult<Response> { ) -> AppResult<Response> {
match posts.get_post(&name).await? { match posts.get_post(&name).await? {
ReturnedPost::Rendered(meta, rendered, rendered_in) => { ReturnedPost::Rendered(ref meta, rendered, rendered_in) => Ok(PostTemplate {
let page = PostTemplate {
meta, meta,
rendered, rendered,
rendered_in, rendered_in,
markdown_access: config.markdown_access, markdown_access: config.markdown_access,
df: config.date_format.clone(), df: &config.date_format,
js: config.js_enable, js: config.js_enable,
}; color: meta.color.as_deref().or(config.default_color.as_deref()),
Ok(page.into_response())
} }
.into_response()),
ReturnedPost::Raw(body, content_type) => { ReturnedPost::Raw(body, content_type) => {
Ok(([(CONTENT_TYPE, content_type)], body).into_response()) Ok(([(CONTENT_TYPE, content_type)], body).into_response())
} }

View file

@ -75,6 +75,7 @@ pub struct Config {
pub markdown_access: bool, pub markdown_access: bool,
pub date_format: DateFormat, pub date_format: DateFormat,
pub js_enable: bool, pub js_enable: bool,
pub default_color: Option<String>,
pub rss: RssConfig, pub rss: RssConfig,
pub dirs: DirsConfig, pub dirs: DirsConfig,
pub http: HttpConfig, pub http: HttpConfig,
@ -90,6 +91,7 @@ impl Default for Config {
markdown_access: true, markdown_access: true,
date_format: Default::default(), date_format: Default::default(),
js_enable: true, js_enable: true,
default_color: Some("#f5c2e7".into()),
// i have a love-hate relationship with serde // i have a love-hate relationship with serde
// it was engimatic at first, but then i started actually using it // it was engimatic at first, but then i started actually using it
// writing my own serialize and deserialize implementations.. spending // writing my own serialize and deserialize implementations.. spending

View file

@ -27,6 +27,8 @@ struct FrontMatter {
pub description: String, pub description: String,
pub author: String, pub author: String,
pub icon: Option<String>, pub icon: Option<String>,
pub icon_alt: Option<String>,
pub color: Option<String>,
pub created_at: Option<DateTime<Utc>>, pub created_at: Option<DateTime<Utc>>,
pub modified_at: Option<DateTime<Utc>>, pub modified_at: Option<DateTime<Utc>>,
#[serde(default)] #[serde(default)]
@ -46,6 +48,8 @@ impl FrontMatter {
description: self.description, description: self.description,
author: self.author, author: self.author,
icon: self.icon, icon: self.icon,
icon_alt: self.icon_alt,
color: self.color,
created_at: self.created_at.or_else(|| created.map(|t| t.into())), created_at: self.created_at.or_else(|| created.map(|t| t.into())),
modified_at: self.modified_at.or_else(|| modified.map(|t| t.into())), modified_at: self.modified_at.or_else(|| modified.map(|t| t.into())),
tags: self.tags.into_iter().collect(), tags: self.tags.into_iter().collect(),

View file

@ -17,6 +17,8 @@ pub struct PostMetadata {
pub description: String, pub description: String,
pub author: String, pub author: String,
pub icon: Option<String>, pub icon: Option<String>,
pub icon_alt: Option<String>,
pub color: Option<String>,
pub created_at: Option<DateTime<Utc>>, pub created_at: Option<DateTime<Utc>>,
pub modified_at: Option<DateTime<Utc>>, pub modified_at: Option<DateTime<Utc>>,
pub tags: Vec<String>, pub tags: Vec<String>,

View file

@ -7,11 +7,16 @@
<meta name="description" content="{{ title }}" /> <meta name="description" content="{{ title }}" />
<meta property="og:title" content="{{ title }}" /> <meta property="og:title" content="{{ title }}" />
<meta property="og:description" content="{{ description }}" /> <meta property="og:description" content="{{ description }}" />
{% match color %} {% when Some with (color) %}
<meta name="theme-color" content="{{ color }}" />
{% when None %} {% endmatch %}
<title>{{ title }}</title> <title>{{ title }}</title>
<link rel="stylesheet" href="/static/style.css" /> <link rel="stylesheet" href="/static/style.css" />
{% if rss %} {% if rss %}
<link rel="alternate" type="application/rss+xml" title="{{ title }}" href="/feed.xml" /> <link rel="alternate" type="application/rss+xml" title="{{ title }}" href="/feed.xml" />
{% endif %} {% if js %} {% endif %}
<!-- prettier-br -->
{% if js %}
<script src="/static/main.js" defer></script> <script src="/static/main.js" defer></script>
{% endif %} {% endif %}
</head> </head>

View file

@ -7,11 +7,26 @@
<meta name="author" content="{{ meta.author }}" /> <meta name="author" content="{{ meta.author }}" />
<meta name="keywords" content="{{ meta.tags|join(", ") }}" /> <meta name="keywords" content="{{ meta.tags|join(", ") }}" />
<meta name="description" content="{{ meta.title }}" /> <meta name="description" content="{{ meta.title }}" />
<!-- you know what I really love? platforms like discord
favoring twitter embeds over the open standard. to color
your embed or have large images, you have to do _this_. lmao -->
<meta property="og:title" content="{{ meta.title }}" /> <meta property="og:title" content="{{ meta.title }}" />
<meta property="twitter:title" content="{{ meta.title }}" />
<meta property="og:description" content="{{ meta.description }}" /> <meta property="og:description" content="{{ meta.description }}" />
<meta property="twitter:description" content="{{ meta.description }}" />
{% match meta.icon %} {% when Some with (url) %} {% match meta.icon %} {% when Some with (url) %}
<meta property="og:image" content="{{ url }}" /> <meta property="og:image" content="{{ url }}" />
<link rel="shortcut icon" href="{{ url }}" /> <meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:image:src" content="{{ url }}" />
{% match meta.icon_alt %} {% when Some with (alt) %}
<meta property="og:image:alt" content="{{ alt }}" />
<meta property="twitter:image:alt" content="{{ alt }}" />
{% when None %} {% endmatch %}
<!-- prettier-br -->
{% when None %} {% endmatch %}
<!-- prettier is annoying -->
{% match color %} {% when Some with (color) %}
<meta name="theme-color" content="{{ color }}" />
{% when None %} {% endmatch %} {% when None %} {% endmatch %}
<title>{{ meta.title }}</title> <title>{{ meta.title }}</title>
<link rel="stylesheet" href="/static/style.css" /> <link rel="stylesheet" href="/static/style.css" />