Files
J621/README.md
T
JakeBreath 770b1e5ee6 Scoped API tokens for the random endpoint, with a management page
Backend: a GreetingToken model stores only a SHA-256 hash of a j621r_…
key (shown once at creation) plus label, prefix, created/last-used. A
dedicated GreetingTokenAuthentication understands the usual
'Authorization: Token …' header but is registered only on RandomItemView
(alongside the normal token auth), so a greeting token authenticates
/api/random/ and is rejected with 401 everywhere else — exactly the scope
shell greetings need. Endpoints: GET/POST /api/auth/greeting-tokens/ and
DELETE /api/auth/greeting-tokens/{id}/ (own tokens only; the list never
returns keys or hashes).

Frontend: /tokens page (Account → Shell tokens card, command palette entry)
lists tokens with label, prefix, created/last-used and revoke (shared
confirm dialog). Creating one shows the key with Copy and 'Copy for fish'
buttons plus a pointer to extras/fish_greeting.

Tests: apps/accounts/tests/test_greeting_tokens.py — 9 tests covering
create-once semantics and hashing, hidden keys in listings, the scope
guarantee (random 200 with a signed URL; 401 on files, storage, me, tags
cloud, delete and the token list itself), unknown/revoked keys, cross-user
revocation, last-used tracking and label limits.

Verified live: created a token, rolled /random (signed URL), got 401 from
four other endpoints, saw the list omit secrets, revoked it (204) and the
same key then 401'd on /random. Full suite: 39 tests green.
2026-09-18 13:37:29 -05:00

93 lines
3.2 KiB
Markdown

# 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
```bash
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
```bash
cd frontend
npm install
npm run dev # http://localhost:5173, proxies /api to :8000
```
### Production
Docker: see [`deploy/`](deploy/README.md) 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):
```bash
curl -H "Authorization: Token <token>" \
"https://j621.example.ts.net/api/random/?rating=s,q&fastfetch=1"
```
```json
{
"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/`](extras/fish_greeting/README.md).
- **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](LICENSE), 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).