JakeBreath a17dd5a4ef Keep secrets and runtime data out of the Docker images
The build context is the repository root and there was no .dockerignore, so
'COPY backend/ ./' swept backend/venv (327 MB), backend/media (the actual
library, 224 MB), backend/logs, backend/staticfiles and backend/.env
(SECRET_KEY plus the database password) into the backend image: 1.43 GB per
architecture, including secrets headed for the registry. The frontend build
stage also copied the host's node_modules over the fresh install.

- Root .dockerignore excludes .git, virtualenvs, __pycache__, db.sqlite3,
  logs/staticfiles, .env files, media/, node_modules, dist and the deploy
  runtime state (data/, tailscale-state/).
- The backend Dockerfile now asserts the context is clean (.env, venv,
  media/library, db.sqlite3 all absent) before collectstatic, so a missing
  ignore file fails the build instead of leaking.
- Rebuilt: backend 1.43 GB -> 876 MB ('COPY backend/' is now 268 kB),
  frontend stays at 65 MB. Verified by booting the compose stack with the
  new image: migrations applied, /health ok, no .env or venv inside, and
  /app/media is the mounted (empty) volume; the scheduler runs too.
- Removed the stale local images that still contained the library and the
  dev .env.
2026-09-18 16:04:55 -05:00
2026-09-18 14:11:10 -05:00
2026-09-18 14:11:10 -05:00

J621

Self-hosted media library and e621 archive manager, rebuilt as a React SPA + Django REST API.

Structure

backend/    Django 6 + DRF API (MariaDB + Redis via docker compose)
frontend/   Vite + React + TypeScript SPA
deploy/     Docker images, compose variants and Tailscale serve configs
extras/     shell integrations (fish_greeting with fastfetch)

Development

Backend

cd backend
source venv/bin/activate
python manage.py migrate
python manage.py scan_files            # index the watched folder
python manage.py runserver

Copy .env.example to .env and set WATCHED_FOLDER before scanning.

Frontend

cd frontend
npm install
npm run dev                            # http://localhost:5173, proxies /api to :8000

Production

Docker: see deploy/ for the two images (SPA on static nginx, API on gunicorn), the three compose variants (both / frontend-only / backend-only) behind a shared nginx service and a Tailscale sidecar, and the public-funnel or tailnet-only serve configs. deploy/push_*.sh builds and pushes the multi-arch images to the Gitea registry.

Random image endpoint

Used by the SPA's Random page and by shell greetings (fish_greeting + fastfetch):

curl -H "Authorization: Token <token>" \
  "https://j621.example.ts.net/api/random/?rating=s,q&fastfetch=1"
{
  "j_id": "J-59",
  "filename": "J-59.jpg",
  "extension": "jpg",
  "rating": "e",
  "url": "https://j621.example.ts.net/api/files/J-59/raw/?sig=…",
  "download_url": "https://j621.example.ts.net/api/files/J-59/raw/?sig=…&download=1",
  "thumbnail_url": "https://j621.example.ts.net/api/files/J-59/thumbnail/?sig=…",
  "fastfetch": true
}
  • rating — comma separated subset of s, q, e (default: any).
  • fastfetch=1, or any request whose User-Agent contains fastfetch, limits the roll to png/jpg/gif so terminals can display it. Images are the only candidates in both modes.
  • url is absolute, and signed for authenticated callers, so fastfetch can load it without headers. Guests get an unsigned URL and only see guest-visible items.
  • /random and /random/ are aliases of /api/random/ for scripts. Behind the bundled nginx those aliases negotiate on Accept: browsers get the SPA page, requesters like curl/wget/fastfetch get the JSON. /api/random/ is the unambiguous path for scripts; 404 when nothing matches the filters.
  • A ready-made shell greeting that uses this endpoint lives in extras/fish_greeting/.
  • Scoped tokens for scripts: the Account page's Shell tokens section (also /tokens) issues j621r_… tokens that only authenticate /api/random/ — the rest of the API rejects them. They are stored as hashes, shown once, and revocable any time (/api/auth/greeting-tokens/).

Licence

Source-available, non-commercial: personal and other non-commercial use is welcome under the Jake Labs Non-Commercial Software Licence, which requires attribution and keeps derivative works under the same licence. Commercial use is not permitted. Third-party dependencies keep their own licences (all permissive: MIT, BSD, Apache-2.0, ISC).

S
Description
No description provided
Readme
1.3 MiB
2026-09-23 22:35:56 -05:00
Languages
TypeScript 55.8%
Python 37.7%
Shell 3.3%
PowerShell 2.5%
Rust 0.5%
Other 0.1%