Bug #954: The initialize_superuser endpoint only checked is_superuser=True, missing admin accounts created via the API (user_level=10). This caused users to intermittently see the "Create Super User" page instead of login.
Feature #1004: Admins can now disable/enable user accounts. Disabled users are blocked from JWT login, token refresh, XC API access, and all
authenticated endpoints. The last active admin cannot be disabled.
Files changed:
Backend:
- apps/accounts/api_views.py — Superuser check uses user_level>=10; token refresh blocks disabled users
- apps/accounts/models.py — CustomUserManager ensures create_superuser() always sets user_level=10
- apps/accounts/serializers.py — Last-admin protection guard; respect is_active on user creation
- apps/accounts/tests.py — new tests covering superuser detection, token
refresh blocking, last-admin protection, disabled user login/access
- apps/backups/api_views.py — Switched from DRF's IsAdminUser (checks is_staff) to app's IsAdmin (checks user_level) on all 8 endpoints
- apps/backups/tests.py — new tests verifying user_level-based admin permission on backup endpoints
- apps/output/views.py — is_active check on xc_get_user(), xc_movie_stream(), xc_series_stream()
- apps/proxy/ts_proxy/views.py — is_active check on stream_xc()
- core/api_views.py — Admin notification filter uses user_level>=10
- core/developer_notifications.py — Admin checks use user_level>=10
Frontend:
- frontend/src/App.jsx — Null-safe superuser existence check
- frontend/src/components/forms/User.jsx — Account Enabled switch with
self-disable prevention and tooltip
- frontend/src/components/tables/UsersTable.jsx — Status column (Active/Disabled badge)
- The User-Agent field was not correctly loaded from the existing profile when opening the edit modal. (Fixes#650)
- Replaced the plain command text field with a dropdown listing built-in tools (FFmpeg, Streamlink, VLC, yt-dlp) plus a Custom option that reveals a free-text input. Each built-in now shows its default parameter string as a live example in the Parameters field description, updating as the command selection changes. Added descriptive help text to all fields to improve clarity.
Add cron expression support for M3U and EPG refreshes with interactive
builder modal and quick reference examples. Refactor backup scheduling
to use shared ScheduleInput component for consistency.
- New CronBuilder modal with preset buttons and custom field editors
- Info popover with common cron expression examples
- Shared ScheduleInput component for interval/cron toggle
- Fix credential extraction in get_transformed_credentials() using negative
indices anchored to the known tail structure instead of hardcoded indices
that break when server URLs contain sub-paths
- Fix EPG URL construction in M3U form to normalize server URL to origin
before appending xmltv.php endpoint
XC accounts with sub-paths in their server URL (e.g., http://server.com/portal/a/) caused two failures: profile refresh extracted wrong credentials from the URL path due to hardcoded array indices, and EPG auto-creation appended xmltv.php under the sub-path instead of at the domain root. Both fixes normalize away the sub-path using the same approach the backend's XCClient already uses for API calls.
- Fixed authentication race condition where `isAuthenticated` was set before data loading completed, causing routes to render and tables to mount prematurely
- Added `isInitialized` flag to delay route rendering until after all initialization data is loaded via `initData()`
- Consolidated version and environment settings fetching into centralized settings store with caching to prevent redundant calls
- Implemented stale fetch prevention in ChannelsTable and StreamsTable using fetch version tracking to ignore outdated responses
- Fixed filter handling in tables to use `debouncedFilters` consistently, preventing unnecessary refetches
- Added initialization guards using refs to prevent double-execution of auth and superuser checks during React StrictMode's intentional double-rendering in development
- Removed duplicate version/environment fetch calls from Sidebar, LoginForm, and SuperuserForm by using centralized store
- Added `is_adult` boolean field to both Stream and Channel models with database indexing for efficient filtering and sorting
- Automatically populated during M3U/XC refresh operations by extracting `is_adult` value from provider data
- Type-safe conversion supporting both integer (0/1) and string ("0"/"1") formats from different providers
- UI controls in channel edit form (Switch with tooltip) and bulk edit form (Select dropdown) for easy management
- XtreamCodes API support with proper integer formatting (0/1) in live stream responses
- Automatic propagation from streams to channels during both single and bulk channel creation operations
- Included in serializers for full API support
- User-level content filtering: Non-admin users can opt to hide mature content channels across all interfaces (web UI, M3U playlists, EPG data, XtreamCodes API) via "Hide Mature Content" toggle in user settings (stored in custom_properties, admin users always see all content)
- Added functionality to pin/unpin table headers, maintaining visibility while scrolling. This feature is accessible via the channel table menu and UI Settings page, with persistence across sessions.
- Refactored table preferences management by migrating `table-size` preference to a centralized `useTablePreferences` hook, enhancing maintainability and consistency across table components.
- Replaced Formik with react-hook-form in Logo, M3UGroupFilter, M3UProfile, Stream, StreamProfile, and UserAgent components.
- Integrated Yup for schema validation in all updated forms.
- Updated form submission logic to accommodate new form handling methods.
- Adjusted state management and error handling to align with react-hook-form's API.
- Ensured compatibility with existing functionality while improving code readability and maintainability.