Introduce username/password authentication with admin and user roles.
Each user gets their own media directory under /photos/{username}/ with
isolated photos, folders, heaps, and tags. Admins manage users and
observe the full library from a dedicated Settings page.
Backend:
- User model with bcrypt passwords and JWT access/refresh tokens
- Auth router (login, refresh, setup, change-password, status)
- Admin router (user CRUD with last-admin protection)
- user_id FK added to photos, folders, source_roots, heaps, tags
- All data routers scoped by authenticated user
- Scanner inherits user_id from source root owner
- Thumbnails stored under user-prefixed paths for isolation
- Library endpoints accept ?scope=global for admin cross-user view
- Alembic migration 0009 with data migration for existing installs
- Defensive bootstrap.py handles fresh vs existing DB startup
Frontend:
- AuthContext with token lifecycle, auto-refresh, login/logout
- Login page, first-run setup page, auth gate in App.tsx
- Bearer token interceptor on all API requests
- User identity + logout in left sidebar
- Admin-only Settings page with Library Management and Users tabs
- UserManagement panel (add, edit role, reset password, deactivate)
- Settings shows global stats across all users for admin
- Filter bar, right sidebar, keyboard hints hidden on settings page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
51 lines
1.8 KiB
YAML
51 lines
1.8 KiB
YAML
# SQLite escape hatch override.
|
|
#
|
|
# Usage (omit the `db` service from the up command):
|
|
#
|
|
# docker compose -f docker-compose.yml -f docker-compose.sqlite.yml \
|
|
# up frontend backend worker redis
|
|
#
|
|
# This pins the backend and worker to the legacy SQLite database file at
|
|
# /data/db/mulita.db (in the existing db_data volume), drops the dependency
|
|
# on Postgres, and skips Alembic — the SQLite schema is still managed by
|
|
# the inline ALTERs in app/database.py:init_db.
|
|
#
|
|
# Vision features that depend on pgvector (PR4 onward) will refuse to enable
|
|
# in this mode; the search/embedding endpoints will return 503 with a clear
|
|
# error pointing back at the default Postgres setup.
|
|
|
|
services:
|
|
backend:
|
|
command: sh -c "uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload"
|
|
environment:
|
|
- DATABASE_URL=sqlite+aiosqlite:////data/db/mulita.db
|
|
- REDIS_URL=redis://redis:6379
|
|
- CELERY_BROKER_URL=redis://redis:6379
|
|
- CELERY_RESULT_BACKEND=redis://redis:6379
|
|
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
|
|
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*}
|
|
- SECRET_KEY=${SECRET_KEY:-mulita-dev-secret-change-me}
|
|
- ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES:-60}
|
|
- REFRESH_TOKEN_EXPIRE_DAYS=${REFRESH_TOKEN_EXPIRE_DAYS:-30}
|
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
|
- TZ=${TZ:-UTC}
|
|
depends_on:
|
|
redis:
|
|
condition: service_started
|
|
|
|
worker:
|
|
environment:
|
|
- DATABASE_URL=sqlite+aiosqlite:////data/db/mulita.db
|
|
- REDIS_URL=redis://redis:6379
|
|
- CELERY_BROKER_URL=redis://redis:6379
|
|
- CELERY_RESULT_BACKEND=redis://redis:6379
|
|
- PHOTO_DIRS=${PHOTO_DIRS:-/photos}
|
|
- CELERYD_CONCURRENCY=${CELERYD_CONCURRENCY:-4}
|
|
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
|
- TZ=${TZ:-UTC}
|
|
depends_on:
|
|
redis:
|
|
condition: service_started
|
|
backend:
|
|
condition: service_started
|