myanime-backend/README.md
Claude 24d59c3f91 feat: PocketBase backend + account/profile schema (epic #6, issue #9)
Foundation for the user-accounts platform. Self-hosted PocketBase v0.39.6,
version-pinned from the official release binary, schema-as-code so the
collections auto-apply on boot and never drift from hand-clicking.

Collections: users (auth, admin-only nsfwEnabled), profiles, watch_state,
watchlist, prefs. Per-owner access rules traverse profile.user; nsfwEnabled
is never client-writable (superuser-only). Relations cascadeDelete; unique
indexes keep one resume/watchlist row per (profile,itemId) and one prefs per
profile.

Verified locally against v0.39.6: migration applies clean, and scripts/verify.py
proves two users can't read/write each other's data and can't set their own
nsfwEnabled (19/19 checks). Coolify deploy (domain/TLS, superuser, SMTP,
backups) documented in README as the manual half.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:22:45 +02:00

5.1 KiB

myanime-backend

PocketBase backend for the myanime app — auth, database, realtime, file storage and admin UI in a single Go binary. This is the foundation the account features build on: cloud sync, codeless device sign-in, profiles, child profiles, recommendations, and gated auto-update (epic #6).

  • No E2E. TLS in transit + at-rest on disk. The server is deliberately readable so recommendations, the admin nsfwEnabled flag, and painless password reset all work.
  • Schema as code. The collections live in pb_migrations/ and auto-apply on boot — never hand-click the schema in the admin UI, or the next deploy will drift.
  • Version-pinned. The Dockerfile downloads the official PocketBase v0.39.6 static binary from GitHub releases (no third-party image). Bump PB_VERSION there and in docker-compose.yml to upgrade.

Data model

users ─┬─* profiles ─┬─* watch_state   (resume points; 1 row per profile+item)
 (auth)│             ├─* watchlist      (1 row per profile+item)
       │             └─1 prefs          (playback/audio/subtitle blob)
       └ nsfwEnabled (admin-only bool → gates adult vs clean build, issue #16)
Collection Type Key fields
users auth username (unique when set), email, password, nsfwEnabled (admin-only)
profiles base user→users, name, avatar (file), isChild, pinHash (hidden)
watch_state base profile→profiles, itemId, itemType, position, duration, updatedAt
watchlist base profile→profiles, itemId, itemType, addedAt
prefs base profile→profiles, data (json)

Access rules. A user only ever reads/writes their own users row, their own profiles, and rows whose profile.user is them. nsfwEnabled is never accepted from a client (@request.body.nsfwEnabled:isset = false on create and update) — only a superuser sets it, from the admin UI. Relations cascadeDelete, so deleting a user removes their profiles and all child rows. Unique indexes keep one resume/watchlist row per (profile, itemId) and one prefs row per profile.

Local run / verify

docker compose up --build            # http://localhost:8090
# create the first superuser (one-time):
docker compose exec pocketbase pocketbase superuser upsert you@example.com 'a-strong-pass' --dir=/pb_data
# admin UI: http://localhost:8090/_/

The end-to-end access-rule check (two users can't see each other's data; a user can't set their own nsfwEnabled; a superuser can) lives in scripts/verify.py:

python scripts/verify.py            # expects the server on :8090 + the superuser above

Deploy on Coolify

The schema and image are automated; the steps below need your hands, Coolify access and secrets (which never live in this repo).

  1. Push this repo to a remote Coolify can reach (e.g. GitHub, like myanime-pair-server).
  2. New Resource → Dockerfile application, point it at this repo. It listens on 8090.
  3. Persistent storage: add a volume mounted at /pb_data (DB + uploaded files). Without this you lose data on redeploy.
  4. Domain + TLS: set the domain to pb.petruzalekr.cz; Coolify/Traefik provisions the Let's Encrypt cert and terminates TLS in front of the plain :8090. Force-HTTPS on.
  5. Bootstrap the superuser once, from the container terminal: pocketbase superuser upsert you@example.com 'a-strong-pass' --dir=/pb_data (or open the printed /_/#/pbinstall/... link on first boot). Lock down the admin UI — strong password; the _superusers collection is the only way in.
  6. SMTP (admin UI → Settings → Mail settings): point at your SMTP host so verification / password-reset emails send. Send the test email to confirm.
  7. Automated backups (admin UI → Settings → Backups): enable the schedule and, ideally, S3 off-site upload. Coolify volume backups are a second layer.
  8. App wiring (later issues, not here): point the app's API base at https://pb.petruzalekr.cz.

Verify the deploy

  • Admin UI reachable over HTTPS only; HTTP redirects up.
  • Create a test user + profile + a couple of rows via admin or REST; confirm a second user can't read them (run scripts/verify.py against the live URL by editing BASE).
  • Trigger a password-reset email and confirm delivery.
  • Confirm a backup ran and can be downloaded/restored.

Security

Account/session tokens and the addon plaintext-credential "tokens" are never logged or echoed (see the app's CLAUDE.md). pinHash is a hidden field and never leaves the API. nsfwEnabled is admin-only by rule, not just by UI.

Upgrading PocketBase

Bump PB_VERSION in the Dockerfile (and the compose arg), rebuild, redeploy. Check the PocketBase release notes for migration-API or schema changes first, and test locally with docker compose up --build before deploying.