- Rust 63.4%
- HTML 12.1%
- JavaScript 11.4%
- CSS 10.7%
- Nix 2.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Each post hangs at its real aspect ratio in one of eight CSS-only gold/brown frame finishes (four plain wood, four ornate gilt with corner rosettes and a beaded molding), packed into a masonry wall via CSS columns. Frame choice is hashed from the post id so it's stable across pages; the wall's old-wallpaper damask texture is one of five color presets picked fresh per page load. Infinite scroll is a single htmx `revealed` sentinel appending pages - no client-side JS of our own. |
||
| crates | ||
| nix | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
booru-server
Single-user, no-auth, Danbooru-shaped local image board. Rust + axum + SQLite,
OLED-black/#E367FC theme, vim-style keyboard navigation. Designed to run
comfortably on a Raspberry Pi.
Tag-enrichment (MD5 hash lookup against public boorus, SauceNAO, and a custom ML tagger) is a separate tool, built on top of this server's write API — not part of this crate.
Develop
nix develop
cargo run -- serve # http://127.0.0.1:8787, data in ./data
cargo run -- import ~/Pictures/Wallpapers # bulk-add untagged posts
Config is TOML, loaded from --config path.toml, overridable via
BOORU_LISTEN, BOORU_DATA_DIR, BOORU_API_TOKEN env vars:
listen = "127.0.0.1:8787"
data_dir = "./data"
# api_token = "..." # only needed once write endpoints are reachable off-loopback
Build / run without a dev shell
nix build
./result/bin/booru-server serve
Deploy (NixOS)
{
inputs.booru-server.url = "path:/SSD/CSdev/booru"; # or a git remote once pushed
# in your host config:
imports = [ inputs.booru-server.nixosModules.default ];
services.booru-server = {
enable = true;
listen = "0.0.0.0:8787";
dataDir = "/var/lib/booru-server";
# apiTokenFile = "/run/secrets/booru-token"; # only if reachable off-loopback
};
}
Cross-compiling to the Pi's aarch64 from an x86_64 dev machine, or building natively on-device, are both fine — not baked into the flake yet.
API surface
Read (open): GET /, GET /posts/:id, GET /tags/autocomplete?q=
Write (bearer token if api_token/BOORU_API_TOKEN is set, open otherwise):
POST /api/posts (multipart: file, optional source, tags),
PATCH /api/posts/:id/tags (JSON {"add": [{"name": "...", "category": "..."}], "remove": ["..."]}),
PUT /api/posts/:id/favorite, DELETE /api/posts/:id.