Compare commits
9 commits
41228d55b6
...
6f7b9b7350
Author | SHA1 | Date | |
---|---|---|---|
6f7b9b7350 | |||
a8a1dca444 | |||
cee11ba07a | |||
38d93a66ba | |||
3623b61fbe | |||
bd093e7c20 | |||
658ddaf820 | |||
342a353b36 | |||
602f57581a |
9 changed files with 76 additions and 35 deletions
29
README.md
29
README.md
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: "README"
|
||||
description: "the README.md file of this project"
|
||||
author: "slonkazoid"
|
||||
title: README
|
||||
description: the README.md file of this project
|
||||
author: slonkazoid
|
||||
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] finish writing this document
|
||||
- [x] document config
|
||||
- [ ] blog thumbnail and favicon
|
||||
- [x] alt text for post icon
|
||||
- [ ] extend syntect options
|
||||
- [ ] general cleanup of code
|
||||
- [ ] 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.
|
||||
# format: https://docs.rs/chrono/latest/chrono/format/strftime/index.html#specifiers
|
||||
js_enable = true # enable javascript (required for above)
|
||||
default_color = "#f5c2e7" # default embed color, optional
|
||||
|
||||
[rss]
|
||||
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]
|
||||
posts = "posts" # where posts are stored
|
||||
media = "media" # directory served under /media/
|
||||
static = "static" # directory server under /static/ (css and js)
|
||||
|
||||
[http]
|
||||
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.
|
||||
the error page will tell you what the problem is.
|
||||
|
||||
example:
|
||||
full example:
|
||||
|
||||
```md
|
||||
---
|
||||
title: "README"
|
||||
description: "the README.md file of this project"
|
||||
author: "slonkazoid"
|
||||
created_at: 2024-04-18T04:15:26+03:00
|
||||
#modified_at: ... # see above
|
||||
title: My first post # title of the post
|
||||
description: The first post on this awesome blog! # short description of the post
|
||||
author: Blubber256 # author of the post
|
||||
icon: /media/first-post/icon.png # icon/thumbnail of post used in embeds
|
||||
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
|
||||
---
|
||||
```
|
||||
|
||||
|
|
50
src/app.rs
50
src/app.rs
|
@ -27,24 +27,26 @@ pub struct AppState {
|
|||
|
||||
#[derive(Template)]
|
||||
#[template(path = "index.html")]
|
||||
struct IndexTemplate {
|
||||
title: String,
|
||||
description: String,
|
||||
struct IndexTemplate<'a> {
|
||||
title: &'a str,
|
||||
description: &'a str,
|
||||
posts: Vec<PostMetadata>,
|
||||
rss: bool,
|
||||
df: DateFormat,
|
||||
df: &'a DateFormat,
|
||||
js: bool,
|
||||
color: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "post.html")]
|
||||
struct PostTemplate {
|
||||
meta: PostMetadata,
|
||||
struct PostTemplate<'a> {
|
||||
meta: &'a PostMetadata,
|
||||
rendered: String,
|
||||
rendered_in: RenderStats,
|
||||
markdown_access: bool,
|
||||
df: DateFormat,
|
||||
df: &'a DateFormat,
|
||||
js: bool,
|
||||
color: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -54,22 +56,24 @@ struct QueryParams {
|
|||
num_posts: Option<usize>,
|
||||
}
|
||||
|
||||
async fn index(
|
||||
async fn index<'a>(
|
||||
State(AppState { config, posts }): State<AppState>,
|
||||
Query(query): Query<QueryParams>,
|
||||
) -> AppResult<IndexTemplate> {
|
||||
) -> AppResult<Response> {
|
||||
let posts = posts
|
||||
.get_max_n_post_metadata_with_optional_tag_sorted(query.num_posts, query.tag.as_ref())
|
||||
.await?;
|
||||
|
||||
Ok(IndexTemplate {
|
||||
title: config.title.clone(),
|
||||
description: config.description.clone(),
|
||||
title: &config.title,
|
||||
description: &config.description,
|
||||
posts,
|
||||
rss: config.rss.enable,
|
||||
df: config.date_format.clone(),
|
||||
df: &config.date_format,
|
||||
js: config.js_enable,
|
||||
})
|
||||
color: config.default_color.as_deref(),
|
||||
}
|
||||
.into_response())
|
||||
}
|
||||
|
||||
async fn all_posts(
|
||||
|
@ -147,18 +151,16 @@ async fn post(
|
|||
Path(name): Path<String>,
|
||||
) -> AppResult<Response> {
|
||||
match posts.get_post(&name).await? {
|
||||
ReturnedPost::Rendered(meta, rendered, rendered_in) => {
|
||||
let page = PostTemplate {
|
||||
meta,
|
||||
rendered,
|
||||
rendered_in,
|
||||
markdown_access: config.markdown_access,
|
||||
df: config.date_format.clone(),
|
||||
js: config.js_enable,
|
||||
};
|
||||
|
||||
Ok(page.into_response())
|
||||
ReturnedPost::Rendered(ref meta, rendered, rendered_in) => Ok(PostTemplate {
|
||||
meta,
|
||||
rendered,
|
||||
rendered_in,
|
||||
markdown_access: config.markdown_access,
|
||||
df: &config.date_format,
|
||||
js: config.js_enable,
|
||||
color: meta.color.as_deref().or(config.default_color.as_deref()),
|
||||
}
|
||||
.into_response()),
|
||||
ReturnedPost::Raw(body, content_type) => {
|
||||
Ok(([(CONTENT_TYPE, content_type)], body).into_response())
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ pub struct Config {
|
|||
pub markdown_access: bool,
|
||||
pub date_format: DateFormat,
|
||||
pub js_enable: bool,
|
||||
pub default_color: Option<String>,
|
||||
pub rss: RssConfig,
|
||||
pub dirs: DirsConfig,
|
||||
pub http: HttpConfig,
|
||||
|
@ -90,6 +91,7 @@ impl Default for Config {
|
|||
markdown_access: true,
|
||||
date_format: Default::default(),
|
||||
js_enable: true,
|
||||
default_color: Some("#f5c2e7".into()),
|
||||
// i have a love-hate relationship with serde
|
||||
// it was engimatic at first, but then i started actually using it
|
||||
// writing my own serialize and deserialize implementations.. spending
|
||||
|
|
|
@ -27,6 +27,8 @@ struct FrontMatter {
|
|||
pub description: String,
|
||||
pub author: String,
|
||||
pub icon: Option<String>,
|
||||
pub icon_alt: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
pub modified_at: Option<DateTime<Utc>>,
|
||||
#[serde(default)]
|
||||
|
@ -46,6 +48,8 @@ impl FrontMatter {
|
|||
description: self.description,
|
||||
author: self.author,
|
||||
icon: self.icon,
|
||||
icon_alt: self.icon_alt,
|
||||
color: self.color,
|
||||
created_at: self.created_at.or_else(|| created.map(|t| t.into())),
|
||||
modified_at: self.modified_at.or_else(|| modified.map(|t| t.into())),
|
||||
tags: self.tags.into_iter().collect(),
|
||||
|
|
|
@ -17,6 +17,8 @@ pub struct PostMetadata {
|
|||
pub description: String,
|
||||
pub author: String,
|
||||
pub icon: Option<String>,
|
||||
pub icon_alt: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
pub modified_at: Option<DateTime<Utc>>,
|
||||
pub tags: Vec<String>,
|
||||
|
|
|
@ -7,11 +7,16 @@
|
|||
<meta name="description" content="{{ title }}" />
|
||||
<meta property="og:title" content="{{ title }}" />
|
||||
<meta property="og:description" content="{{ description }}" />
|
||||
{% match color %} {% when Some with (color) %}
|
||||
<meta name="theme-color" content="{{ color }}" />
|
||||
{% when None %} {% endmatch %}
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
{% if rss %}
|
||||
<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>
|
||||
{% endif %}
|
||||
</head>
|
||||
|
|
|
@ -7,11 +7,26 @@
|
|||
<meta name="author" content="{{ meta.author }}" />
|
||||
<meta name="keywords" content="{{ meta.tags|join(", ") }}" />
|
||||
<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="twitter:title" content="{{ meta.title }}" />
|
||||
<meta property="og:description" content="{{ meta.description }}" />
|
||||
<meta property="twitter:description" content="{{ meta.description }}" />
|
||||
{% match meta.icon %} {% when Some with (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 %}
|
||||
<title>{{ meta.title }}</title>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
|
|
Loading…
Reference in a new issue