- README: expand the production section with a full systemd recipe (useradd, chown, uv sync --project, install -d for ReadWritePaths, .env perms 640), a Caddy block, and backup note. - AGENTS.md: add the Arch python-uv package-name note and the root-rooted venv caveat, plus a new "Deploy / production" section documenting the volundr unit, data location, .env perms, and forward-proxy expectations.
101 lines
3.6 KiB
Markdown
101 lines
3.6 KiB
Markdown
# volundr
|
|
|
|
A small, secure, single-user web app for logging bike service and repair
|
|
"events" (type, date, mileage, cost, notes) and getting a clear **service-due**
|
|
hint per bike, plus **MOT / insurance renewal** tracking.
|
|
|
|
Built with FastAPI + SQLite + Jinja2 + HTMX. No build step. Server-rendered pages.
|
|
|
|
## Features
|
|
|
|
- Log events for a bike: type (service, fuel, tyre, chain, brake, repair, mot,
|
|
insurance, other), date, optional mileage + cost, free-text notes.
|
|
- Per-bike **service status** derived from your newest mileage and the most recent
|
|
*service* event: `ok` (still under the interval), `due-soon` (within the lead
|
|
time — book it soon), or `overdue` (past it — book a service now).
|
|
- **MOT / insurance renewals**: the most recent `mot` / `insurance` event is
|
|
tracked and flagged `ok` / `due-soon` / `overdue` by date (default: valid for 12
|
|
months, "due soon" within the final 60).
|
|
- Set a **service interval** in miles per bike (e.g. 5000).
|
|
- Dashboard "action needed" strip surfaces any overdue bike.
|
|
- Cost and event totals, newest-first history.
|
|
- **Secure login** (Argon2id hashing, server-side session, CSRF, login
|
|
throttling, hardened security headers, strict CSP). Designed to run behind a
|
|
reverse proxy on the open web.
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# 1. Install dependencies
|
|
uv sync --all-extras
|
|
|
|
# 2. Configure credentials (required)
|
|
export BIKE_ADMIN_USERNAME=admin
|
|
export BIKE_ADMIN_PASSWORD=<change-me>
|
|
export BIKE_ADMIN_SECRET_KEY=$(python -c "import secrets;print(secrets.token_urlsafe(32))")
|
|
|
|
# 3. Run
|
|
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000
|
|
# open http://127.0.0.1:8000
|
|
```
|
|
|
|
Or copy `.env.example` to `.env` and fill in the values (env vars and the local
|
|
`.env` file are both read automatically).
|
|
|
|
## Running behind a reverse proxy (production)
|
|
|
|
TLS terminated at the proxy (Caddy/nginx), app on a private host behind it.
|
|
`deploy/bike_admin.service` is the systemd unit used in production;
|
|
`deploy/env.production.example` is the production `.env` template.
|
|
|
|
- Set `BIKE_ADMIN_IS_PRODUCTION=true` so the session cookie is `Secure`.
|
|
- Bind as needed for the proxy to reach you:
|
|
- proxy on the **same** host → `--host 127.0.0.1`
|
|
- proxy on **another** host (e.g. a single fronting Caddy) → `--host 0.0.0.0`
|
|
or bind to a specific LAN interface; never the public interface directly, since
|
|
there is no app-level TLS.
|
|
- Proxy forwards to the app and sets `X-Forwarded-For` / `X-Forwarded-Proto`;
|
|
the app trusts these by default (`BIKE_ADMIN_TRUST_PROXY`). uvicorn's
|
|
`--forwarded-allow-ips 192.168.0.0/16 --proxy-headers` accepts them
|
|
(adjust to your LAN range).
|
|
|
|
### systemd (production recipe)
|
|
|
|
```bash
|
|
# one-time: deps + service account [on Arch: uv ships as python-uv]
|
|
sudo pacman -S --needed git python-uv
|
|
sudo useradd --system bike
|
|
|
|
# one-time: app
|
|
sudo chown -R bike:bike /opt/bike_admin
|
|
sudo -u bike uv sync --all-extras --project /opt/bike_admin
|
|
sudo install -d -o bike -g bike /opt/bike_admin/data # systemd needs it to exist
|
|
|
|
# one-time: .env (see deploy/env.production.example)
|
|
sudo chown bike:bike /opt/bike_admin/.env
|
|
sudo chmod 640 /opt/bike_admin/.env # owner rw, group r, others none
|
|
|
|
sudo cp deploy/bike_admin.service /etc/systemd/system/bike-admin.service
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now bike-admin
|
|
```
|
|
|
|
Caddy block for the fronting proxy:
|
|
|
|
```
|
|
volundr.local.jakes-asgard.uk {
|
|
reverse_proxy 192.168.0.52:8000
|
|
}
|
|
```
|
|
|
|
Back up your data by copying the single file in the data dir —
|
|
`<data_dir>/bike_admin.db` (SQLite, WAL; `data/bike_admin.db` by default).
|
|
|
|
## Tests / lint
|
|
|
|
```bash
|
|
uv run pytest -q
|
|
uv run ruff check app tests
|
|
uv run ruff format --check app tests
|
|
```
|