Introduces a manifest contract so feature containers self-describe their
settings (JSON Schema + access rules). Backend and frontend gain generic
plugin proxy and dynamic Extensions UI with zero feature-specific code.
Doc-service is the first plugin consumer: exposes /plugin/manifest and
/plugin/settings, adds a watchdog-based file watcher that auto-ingests
PDFs from a mounted directory, maps subfolders to categories, supports
AI-suggested folder/filename (user-confirmed), and enforces a no-remove
policy. Access is gated by is_superuser or doc-service-admin group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- deps.py: get_current_admin returns 404 Not Found for non-superusers instead
of 403 Forbidden — hides endpoint existence from unauthorised callers
- App.tsx: AdminRoute redirects non-admins to /login instead of /, making
the route indistinguishable from a non-existent page
Layer 3 (network-level IP restriction via Traefik) tracked in TODO.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- schemas/user.py: is_admin (validation_alias=is_superuser) on UserOut and
UserAdminOut; UserAdminCreate extends UserCreate with is_admin flag
- deps.py: get_current_admin dependency — 403 for non-superusers
- routers/admin.py: GET/POST /api/admin/users, DELETE and PATCH /active per
user; self-delete and self-deactivate blocked
- main.py: register /api/admin router
- scripts/seed.py: seed test user with is_superuser=True; promotes existing
user if already created without the flag
Frontend:
- api/client.ts: UserData type with is_admin, admin API functions
- components/Nav.tsx: Admin link visible only when user.is_admin is true
- pages/AdminPage.tsx: user table with add-user form, delete, toggle active
- App.tsx: AdminRoute guard (403-redirects non-admins to /); /admin route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>