Coolify attaches a root-owned volume/bind at /pb_data; the non-root pb user couldn't write it, so SQLite failed with "unable to open database file (14)". Drop the pb user and run as root. Verified against a root-owned bind mount locally: server starts clean, migrations apply, data.db persists to the mount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| pb_migrations | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
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
nsfwEnabledflag, 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
Dockerfiledownloads the official PocketBase v0.39.6 static binary from GitHub releases (no third-party image). BumpPB_VERSIONthere and indocker-compose.ymlto 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).
- Push this repo to a remote Coolify can reach (e.g. GitHub, like
myanime-pair-server). - New Resource → Dockerfile application, point it at this repo. It listens
on
8090. - Persistent storage: add a volume mounted at
/pb_data(DB + uploaded files). Without this you lose data on redeploy. - 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. - 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_superuserscollection is the only way in. - SMTP (admin UI → Settings → Mail settings): point at your SMTP host so verification / password-reset emails send. Send the test email to confirm.
- Automated backups (admin UI → Settings → Backups): enable the schedule and, ideally, S3 off-site upload. Coolify volume backups are a second layer.
- 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.pyagainst the live URL by editingBASE). - 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.