PostgreSQL now runs as the PUID/PGID user ($POSTGRES_USER) instead of the internal postgres system user (UID 102). This fixes container startup failures when PUID/PGID is set, caused by chown permission errors on restricted filesystems (NFS root_squash, CIFS) and UID collisions with the postgres system user.
Changes:
- Run all PostgreSQL operations (initdb, pg_ctl, psql) as $POSTGRES_USER
- Auto-detect PUID/PGID from existing data owner when not explicitly set
- Validate PUID/PGID (reject zero, non-numeric values) before startup
- Migrate existing data ownership with sentinel-based skip optimization
- Use trust auth for local Unix sockets, md5 for network connections
- Add promote_app_role() and ensure_app_database() as idempotent startup guarantees that handle fresh installs, upgrades, and PUID changes
- Preserve postgres role as superuser for rollback compatibility
- Centralize /data/db ownership in 02-postgres.sh (sentinel-aware)
- Add integration test suite (20 scenarios) covering fresh installs, upgrades, restarts, PUID changes, UID collisions, bind mounts, modular mode, PG major upgrades, and end-to-end web UI verification
P1 (Critical):
- Replace undefined @swagger_auto_schema with @extend_schema
- Add custom_properties validation with size limits
- Add merge semantics in serializer update() to prevent data loss
P2 (Important):
- Add debouncing (800ms) for drag operations in NavOrderForm
- Add error handling with notifications for visibility toggle
- Use store getters in Sidebar instead of direct property access
- Cache hiddenNav before render loop
P3 (Nice-to-have):
- Add canHide: false to settings nav item
- Fix admin check to use >= instead of ==
- Fix ESLint unused variable warnings
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add eye icon toggle to show/hide nav items in settings panel
- Hidden items stay in position but are dimmed with eye-slash icon
- Sidebar filters out hidden items
- Settings item cannot be hidden
- Reset to Default also clears hidden items
- Add getHiddenNav and toggleNavVisibility to auth store
- Update tests for visibility toggle feature
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Navigation accordion section to Settings page
- Update Sidebar to use getOrderedNavItems() with user's saved order
- Sidebar updates immediately when order changes
- Add test coverage for Navigation settings section
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Drag-and-drop reorderable list using dnd-kit
- Auto-saves on drop with optimistic update
- Reset to Default button restores role-based defaults
- Shows only items available to user's role
- Add test coverage for admin and non-admin users
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add updateMe() API method for PATCH requests to /me/
- Add getNavOrder() to retrieve saved navigation order
- Add setNavOrder() to persist navigation order changes
- Add updateUserPreferences() with optimistic updates and rollback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract nav item definitions into config/navigation.js
- Define NAV_ITEMS with id, label, icon, path, adminOnly for each item
- Add DEFAULT_ADMIN_ORDER and DEFAULT_USER_ORDER arrays
- Create getOrderedNavItems() helper for custom ordering
- Handle missing items by appending to end of saved order
- Add comprehensive test coverage
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Allow users to update their own profile via PATCH /api/accounts/users/me/
- Supports partial updates to custom_properties (including navOrder)
- Add comprehensive test coverage for the endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Stream.get_stream() was never called in the preview path of
generate_stream_url(), so the channel_stream and stream_profile
Redis keys were never written. This caused the stream_id to be
missing from channel metadata, which silently broke two things:
- Stats page showed no stream name or logo
- stream_stats were never saved to the database
Fix: replace the manual profile-selection loop in the preview path
with a direct call to Stream.get_stream(), matching the channel path
exactly. This also fixes a pre-existing non-atomic slot reservation
(read-then-check vs INCR-first) that could over-allocate connections
on concurrent previews.
Regression introduced in 49b7b9e2.