multi-user auth (CLI-created accounts), per-user history, test suite

- server/users.py replaces credentials.py: users.json (0600), PBKDF2,
  one-time migration of the legacy single password to user admin
  (history.json -> history/admin.json)
- accounts are CLI-managed only: python -m server.users add|rm|list
  (no self-signup on an internet-exposed app); /signup route removed,
  login page back to username + password
- per-user 30-day signed session cookie (payload carries the
  username), per-user lockout, GET /api/me, POST /account/delete
- per-user watch history: server/data/history/<user>.json; ANIWEB_DATA
  env override for the data dir
- header chip: logged-in user, logout, delete account
- tests/: throwaway-server suite (port 8399, temp data dir) — 9 HTTP
  tests + 2 Playwright browser tests (login UI, real video playback
  behind auth); runs via python tests/run.py
This commit is contained in:
jozamudi committed 2026-08-19 16:17:56 +00:00
1 parent 76c45a8f29
commit c697500c7d
12 files changed
+595 -122

No files matched your search

+15 -12
View File
@@ -51,19 +51,23 @@ Two moving parts, no database, no framework on the frontend.
- `GET /api/anime/{id}/episodes` → `[{id, number, filler}]` (step 3, passthrough)
- `GET /api/episode/{ep_id}/stream?lang=eng|jpn` → `{token, qualities}`
(steps 5+6; token is base64url of the master m3u8 URL)
- `GET /api/history` / `POST /api/history` → watch history with
position, persisted to `server/data/history.json` (50 kept, top 5
returned; upsert per anime+episode)
- `GET /api/history` / `POST /api/history` → **per-user** watch
history with position, persisted to
`server/data/history/<user>.json` (50 kept, top 5 returned;
upsert per anime+episode).
- `GET /stream/{token}` → proxied HLS. Token = base64url of the
upstream URL. Rewrites every non-comment line of m3u8 bodies to
point back at `/stream/`; proxies master, variants, and segments
(bytes, no transcoding).
- `GET /login` / `POST /login` / `POST /logout` → single-user auth:
PBKDF2-SHA256 password (`server/credentials.py`; hash + session
secret in `server/data/credentials.json`, mode 0600), signed 30-day
cookie, 5-fail/15-min in-memory lockout. Everything else 401s
(API) or redirects to `/login?next=` (pages). Server refuses to
start without credentials.
- `GET /login` / `POST /login` / `POST /logout` → multi-user auth:
PBKDF2-SHA256 passwords (`server/users.py`; `users.json` 0600),
signed 30-day cookie carrying the username, 5-fail/15-min
in-memory lockout **per user**. Accounts are CLI-created only
(`python -m server.users add|rm|list`) — no self-signup. One-time
migration: legacy single-password `credentials.json` → user
`admin`; `history.json` → `history/admin.json`.
- `GET /api/me`, `POST /account/delete` → current user / self-delete
(account + history file).
## Frontend
@@ -95,9 +99,8 @@ Open `http://localhost:8321` (bound to 0.0.0.0 for LAN access).
## Out of scope (v1)
Downloads (`-d`), next-episode countdown (`-N`), syncplay, intro skip,
ani-schedule integration, multi-user accounts (single shared
password today — per-user history is the natural upgrade), episode
titles (not in
ani-schedule integration, episode titles (not in anidb's API — needs
a MyAnimeList API key). Add when someone asks.
anidb's API — needs a MyAnimeList API key). Add when someone asks.
## Risks