A lightweight blog platform, written in Haskell using Yesod. Using Redis for caching data and sqlite for providing sorted data.
This branch does not includes any CSS for web pages. If you're looking for already designed site, check main-bulma
branch.
- Fully SSR! 🚀
- Posts stored in MD and YAML files
- Sitemap.xml autogeneration
- Internal Redis caching
- Headers ID autogeneration
- OpenGraph and JSON-LD generation for pages
- Generation of table of contents
- Option to disable header's id autogeneration
- React on 0 seconds cache time as its disabling
The main idea of project is storing posts of blog in Markdown files in templates
directory, so the name
of files and folders will be URL of page. Each post consists of two files:
- Markdown file, which includes page content (sample file)
- YML file, which inclues information about post like title, image, description etc. (sample file)
For example, post https://YOURSITE.HERE/post/haskell/how-to-start-yesod
will be stored in files
./templates/haskell/how-to-start-yesod.md
and ./templates/haskell/how-to-start-yesod.yml
.
Parsing of markdown file written in Attoparsec in Parser
module
and theoretically can be extended. Now, its implemented basic elements of Markdown like headers, lists, code blocks, quotes,
images, links, bold and italic texts.
Result of parsing both files is stored in Redis for one minute. This timing can be changed in Redis module.
Also, blog-hs stores data about posts in SQLite database, which is used for showing latest posts on index page and generating
sitemap.xml file. Database is creating manually using create-db
command and stores data from YML files.
Currently it is not available for configuration. Each header gets id from its content - images transformed into its alternative text, text format is ignored. All text is converting into lowercase and gets joined using dash. Punctuation marks is ignored.
Next to each header will be paragraph symbol, which allows to go to this header.
All details specified in Parser/Inline.hs
and Parser/Html.hs
modules.
# This header will be transformed into this HTML code:
<h1 id="this-header-will-be-transformed-into-this-html-code"> This header will be transformed into HTML code: </h1>
Some symbols (like russian alphabet by default) gets translated into english symbols. All symbols
except this by default will be ignored. For correcting this behavior see transliterateCharacter
function.
Post info always stored in .yml (not .yaml) file with same name and have following fields:
Field | Type | Description |
---|---|---|
name | string | Post title |
description | string | Post description |
date | string | Post date in format of "YYYY-MM-DD" |
images | list of string | Photo URLs list for post preview, opengraph and etc. |
Sample file is example.yml.
Each post can be attached to different categories using categories
section. It must contain list of strings, which is
category names. Using categories, you can group different posts and display then on post category screen.
Posts of current categories can be accessed by URL /category/<category name>
. It displays 10 posts per page by default.
-
-f <PATH>
: sets path for config file -
-p <PORT>
: sets port for server to listen. Default is 3000. -
-c
/--create-db
: create SQLite database before starting the server. Flag forrun
command.
Blog-hs currently have several commands:
- check: validates existence of YML files for every MD file in
./templates/
directory (note that its relative path and depends on your working directory).
- create-db: creates (or replaces) SQLite database. Also validates files first.
- run: launches the server.
Configuration file can be passed using -f
flag. In other case, server will take important parameters from environment
variables or will use default values.
Field in config file | Environment variable name | Usage | Default value |
---|---|---|---|
redisHost | REDIS_HOST | Address for Redis host | localhost |
redisPort | REDIS_PORT | Port of redis server | 6379 |
dbPath | DB_PATH | Path of SQLite database | ./blog.db |
blogDepthLimit | DEPTH_LEVEL | How deep is allowed to go into folders. If request will try to get file deeper than limit, server will response PermissionDenied error. | 1 |
siteName | SITE_NAME | Short description of your website. Used in OG and JSON-LD | - |
siteHost | SITE_HOST | Base host of site. For example, "https://mrtstg.ru". No trailing slash at end. Used in generating links. | - |
robotsFilePath | ROBOTS_TXT_PATH | Path for robots.txt file. | - |
redisCacheTime | REDIS_CACHE_TIME | Time in seconds at what period Redis must store parsed port | 60 |
If nececcary, you can disable some pages, like index or category page. In this case, you should use disabledPages
option in
configuration file. It accepts list of strings, where:
index
is index page optioncategory
/categories
is category page option. Also, it disables link generation on categories on post page.robots
/robots.txt
is robots.txt route.sitemap
/sitemap.xml
is sitemap.xml route.
In other case, you can use ENABLE_INDEX
and ENABLE_CATEGORIES
environment variables, which accept
1
as True or any other value as False.
Configuration file also contains categories
section. Its consists of objects of following scheme:
name: "category-name-used-in-URL"
displayName: "Category name, which will be displayed on website"
description: "Category description"
Currently, there is no way to customize categories using environment.
By default, web server renders at the top of the page post title, date and categories. This behavior
can be changed using renderSettings
section of server config.
This section has fields renderTitle
, renderDate
and renderCategories
,
which are controlling the behavior. If field is not presented, value is True
by default.
You can disable rendering OpenGraph and JSON-LD information using field renderMeta
.
renderSettings:
renderDate: false
renderTitle: true
renderMeta: false
# renderCategories: true by default
Instead of launching built binary there is way for launch server in Docker container using Docker Compose.
If you have make
, you make it using:
make build-image
make deploy
For destroying containers, use make destroy
command.
This method by default suggests that you will configure server using environment variables. Add volume for config file if its nececcary.
If you want only to build image, use make build-image
command.
❗ Dont forget to correct robots.txt file! It's not getting generated automatically.
Code is under BSD 3 Clause license. Please have a look at the LICENSE for details.