Initial commit, blog system works
This commit is contained in:
155
README.md
Normal file
155
README.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# btmx
|
||||
|
||||
A self-contained hypermedia-driven blogging platform in a single portable binary. Written in Lua for the [RedBean](https://redbean.dev/) webserver, routed with [Fullmoon](https://github.com/pkulchenko/fullmoon), rendered as hypermedia with [HTMX](https://htmx.org/), and stored in SQLite. The build produces one executable (`blog.com`) that runs on Linux, BSD, macOS, and Windows; content lives in `blog.db` next to it.
|
||||
|
||||
## Features
|
||||
|
||||
- **Single binary:** no dependencies, no installation, no configuration files. Just run `./blog.com` and it just works™.
|
||||
- **Markdown support:** posts are Markdown, rendered into HTML. [highlight.js](https://highlightjs.org/) is included for code blocks.
|
||||
- **Radio Station:** web radio included as an optional feature for a given .m3u playlist URL.
|
||||
- **RSS/Atom:** Atom feed is generated automatically at `/atom.xml`.
|
||||
- **Themes:** attempts to respect user preferences for light or dark mode. Also includes a retro theme.
|
||||
- **Admin dashboard:** `/edit` does almost everything needed to manage the blog.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GPL-3.0 License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Requirements
|
||||
|
||||
All requirements are pulled during maketime with `curl` using GNU `make` (if Justine has not updated her TLS certs, you may need to grab redbean by hand.)
|
||||
Because it's awesome, the fatbin should work robustly on most OSes; daemons may need to use the `ape` loader, rather than running the binary directly.
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
git clone https://git.asperger.pro/hpcdisrespecter/btmx.git
|
||||
cd btmx
|
||||
make # fetch redbean/fullmoon/htmx/highlight.js/fonts and build blog.com
|
||||
make setup # set the admin password (creates blog.db)
|
||||
```
|
||||
|
||||
There is nothing to edit before building: all site identity (title, author, base URL, …) is configured after first login at `/edit/settings`.
|
||||
|
||||
## Running
|
||||
|
||||
### Configuration
|
||||
|
||||
Resetting the password and setting up the database:
|
||||
|
||||
```bash
|
||||
./blog.com -i setup_admin.lua
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
./blog.com
|
||||
```
|
||||
|
||||
Runs on `localhost:8080` and attempts to open your default browser to the blog (set `BLOG_NO_BROWSER=1` to suppress). Note that the Lua application is zipped **inside** `blog.com`; after editing anything under `.lua/` or `static/`, run `make` again.
|
||||
|
||||
### HTTPS
|
||||
|
||||
```
|
||||
./blog.com -p 443 \
|
||||
-K privkey.pem \
|
||||
-C fullchain.pem \
|
||||
-J
|
||||
```
|
||||
|
||||
Runs on `localhost:443` with TLS and mandatory HTTPS using the provided certificates.
|
||||
|
||||
### Daemon
|
||||
|
||||
#### Systemd Service
|
||||
|
||||
First, produce a daemon user and copy the application files to its home directory:
|
||||
|
||||
```bash
|
||||
sudo useradd -r -s /usr/sbin/nologin -d /var/lib/blog blog
|
||||
cp blog.com /var/lib/blog/blog.com
|
||||
cp blog.db /var/lib/blog/blog.db
|
||||
sudo chown -R blog:blog /var/lib/blog
|
||||
```
|
||||
|
||||
Then, deploy your TLS certificates to `/var/lib/blog/privkey.pem` and `/var/lib/blog/fullchain.pem` with similar permissions. Consider using a hook in your certificate management tool to automate this process.
|
||||
|
||||
Finally, make sure your system has a working [ape](https://justine.lol/ape.html) loader.
|
||||
```
|
||||
which ape
|
||||
```
|
||||
|
||||
Create `/etc/systemd/system/blog.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Hypermedia Blog (Redbean)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=blog
|
||||
WorkingDirectory=/var/lib/blog
|
||||
Environment=BLOG_NO_BROWSER=1
|
||||
ExecStart=ape \
|
||||
/var/lib/blog/blog.com -p 443 -p 80 \
|
||||
-K /path/to/privkey.pem \
|
||||
-C /path/to/fullchain.pem \
|
||||
-J
|
||||
Restart=on-failure
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Then enable and start the service:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable blog.service
|
||||
sudo systemctl start blog.service
|
||||
```
|
||||
|
||||
#### OpenRC Service
|
||||
|
||||
As above, you produce a daemon user, a copy of the application files, and deploy your TLS certificates.
|
||||
|
||||
Then, create `/etc/init.d/blog`:
|
||||
|
||||
```bash
|
||||
#!/sbin/openrc-run
|
||||
command="/usr/bin/ape"
|
||||
command_args="/var/lib/blog/blog.com -p 443 -p 80 \
|
||||
-K /path/to/privkey.pem \
|
||||
-C /path/to/fullchain.pem \
|
||||
-J"
|
||||
command_user="blog:blog"
|
||||
export BLOG_NO_BROWSER=1
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
```
|
||||
Make the script executable and add it to the default runlevel:
|
||||
|
||||
```bash
|
||||
sudo chmod +x /etc/init.d/blog
|
||||
sudo rc-update add blog default
|
||||
```
|
||||
|
||||
Finally, start the service:
|
||||
|
||||
```bash
|
||||
sudo rc-service blog start
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **Database**: `blog.db` is created automatically at first request in the working directory. Back it up and you have backed up the whole site (posts, uploads, settings, password hash).
|
||||
- **Admin access**: Visit `/login`, enter your password. A session cookie (HttpOnly, SameSite=Strict, 7-day TTL) is set on success. Sessions are stored in `blog.db` and survive server restarts. `/edit` is the admin dashboard; `/edit/settings` configures the site.
|
||||
- **Atom feed**: set the **Base URL** in Settings to your public origin (e.g. `https://example.com`) so feed IDs and links are absolute.
|
||||
- **Rate limiting**: Login is limited to 5 attempts per IP per 15 minutes (in-memory; resets on restart).
|
||||
- **CSRF tokens**: Every mutating form includes a `csrf_token` hidden field validated against an HttpOnly SameSite=Strict cookie.
|
||||
- **Password changes**: Re-run `./blog.com -i setup_admin.lua` at any time. The new hash overwrites the old one immediately.
|
||||
Reference in New Issue
Block a user