102 lines
3.8 KiB
Markdown
102 lines
3.8 KiB
Markdown
# Narlblog
|
|
|
|
A single-author blog. Rust/Axum API backed by markdown files on disk; Astro/React frontend with a glass-effect Catppuccin theme. KaTeX math, GFM tables, server-side syntax highlighting, draft posts, RSS feed.
|
|
|
|
```
|
|
backend/ Rust + Axum API (filesystem-backed)
|
|
frontend/ Astro + React, SSRs pages and proxies /api/*
|
|
data/ Runtime data — posts, uploads, config.json (host volume)
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
# Generate a strong admin token:
|
|
echo "ADMIN_TOKEN=$(openssl rand -hex 32)" >> .env
|
|
|
|
docker compose up --build
|
|
# → http://localhost:4321
|
|
# → log in at /admin/login with the token from .env
|
|
```
|
|
|
|
Make sure the `data/` host directory is owned by UID 1000 (the backend container's app user):
|
|
|
|
```sh
|
|
sudo chown -R 1000:1000 data/
|
|
```
|
|
|
|
## Environment
|
|
|
|
| Variable | Required | Default | Notes |
|
|
| ----------------- | -------- | ----------------- | --------------------------------------------------------------------------- |
|
|
| `ADMIN_TOKEN` | yes | — | Long random string. Stored as an HttpOnly cookie after login. |
|
|
| `PORT` | no | `3000` | Backend port. |
|
|
| `DATA_DIR` | no | `/app/data` | Where posts/uploads/config live. |
|
|
| `COOKIE_SECURE` | no | `true` | Set `false` only for local HTTP development. |
|
|
| `FRONTEND_ORIGIN` | no | _empty_ | Set to your frontend's URL only if you expose the backend directly. |
|
|
| `RUST_LOG` | no | `info` | tracing-subscriber filter. |
|
|
| `PUBLIC_API_URL` | no | `http://backend:3000` | Backend URL the Astro proxy hits server-to-server. |
|
|
|
|
## Local development
|
|
|
|
Backend:
|
|
|
|
```sh
|
|
cd backend
|
|
ADMIN_TOKEN=devtoken COOKIE_SECURE=false DATA_DIR=../data cargo run
|
|
```
|
|
|
|
Frontend (separate terminal, Node ≥ 22.12):
|
|
|
|
```sh
|
|
cd frontend
|
|
npm install
|
|
npm run dev
|
|
# → http://localhost:4321 (proxies to PUBLIC_API_URL, default http://backend:3000)
|
|
```
|
|
|
|
For a fully local stack, set `PUBLIC_API_URL=http://localhost:3000` in `frontend/.env`.
|
|
|
|
## Authoring posts
|
|
|
|
Posts are markdown files at `data/posts/<slug>.md` with YAML frontmatter:
|
|
|
|
```markdown
|
|
---
|
|
date: 2026-05-09
|
|
summary: Optional summary; falls back to auto-extracted excerpt.
|
|
tags:
|
|
- rust
|
|
- astro
|
|
draft: false
|
|
---
|
|
# My post
|
|
|
|
Body in **markdown**, with $LaTeX$ via `$…$` / `$$…$$`, GFM tables, and fenced code blocks (```rust, ```ts, …).
|
|
```
|
|
|
|
Posts with `draft: true` are hidden from the public list and 404 when accessed by anyone without an admin session. Posts are sorted by `date` descending on the frontpage.
|
|
|
|
The web editor at `/admin/editor` writes the same format and updates atomically (write to `.tmp`, rename over target).
|
|
|
|
## Uploads
|
|
|
|
Allowlisted extensions: jpg, jpeg, png, webp, gif, avif, pdf, txt, md, mp3, wav, ogg, mp4, webm, mov. Magic bytes are checked against the extension. SVG and HTML are intentionally rejected — `/uploads/*` is served as-is, so any active content there would be XSS.
|
|
|
|
Max upload size: 50 MB.
|
|
|
|
## Backups
|
|
|
|
The deployed `data/` directory is the entire blog. Back it up with whatever you trust — `rsync`, restic, borg, a sidecar container; nothing fancy is built in.
|
|
|
|
```sh
|
|
rsync -av data/ backup-host:/path/to/narlblog-data/
|
|
```
|
|
|
|
## Stack
|
|
|
|
- **Backend**: Rust 2024 edition (requires 1.85+), axum 0.8, serde_yaml frontmatter, subtle constant-time auth, infer for upload sniffing
|
|
- **Frontend**: Astro 6, React 19, Tailwind 4, marked + marked-katex-extension + marked-highlight + DOMPurify
|
|
- **Editor**: CodeMirror 6 with vim mode and asset autocomplete
|