Commit Graph

274 Commits

Author SHA1 Message Date
Matthieu B
7803cfd74c Keep invite form open on errors
Keep form-based invitation screens open on account creation failures so users can see errors without replaying the welcome animation.

Centralize join form template context, preserve theming across invite routes, and add regression coverage for validation and E2E flows.
2026-07-05 18:53:49 +02:00
Matthieu B
c10a0e1911 Validate invitation form submissions (#1322)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-05 18:37:15 +02:00
Matthieu B
31005dcf87 Fix recent onboarding and media-server bugs (#1318)
* fix: resolve button widget context URLs

Fixes #1280

* fix: return relative invitation API URLs

Fixes #1262

* fix: use modern Jellyfin auth headers

Fixes #1309

* fix: use Emby library GUIDs for access policy

Fixes #1303
2026-07-05 18:14:39 +02:00
Matthieu B
26f99b7532 Merge pull request #1304 from ZRiddle/fix/unlimited-invite-multi-user-tracking
Fix: unlimited invite only shows first user in Latest Accepted Invites
2026-07-05 17:50:20 +02:00
zriddle
d30a3bdc23 Fix unlimited invite only recording first user in Latest Accepted Invites
When multiple users accept the same unlimited invite code, the user lookup
after a successful join used `filter_by(code=...).first()`, which always
returned the first user who ever used that code on the server — not the
user who just signed up. Subsequent users were never inserted into
invitation_users, so the home page card only ever showed the first acceptee.

Fix: look up the newly created user by username (unique per server) so each
acceptance is correctly attributed.

Add a regression test that asserts all users appear in invite.users after
multiple acceptances of an unlimited invite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:00:08 -06:00
Danni Croax
8e680388dd fix: handle multi-plex oauth invites 2026-06-06 12:40:31 +00:00
Matthieu B
4847133026 fix: restore get_recent_items parameter names broken by linting pass
Commit 73c29458f renamed library_id/limit to _library_id/_limit to
suppress ARG002 warnings, but the widget caller passes them as keyword
arguments (limit=6), causing a silent TypeError that returns empty data.

Closes #1207
2026-04-01 22:29:39 +02:00
Matthieu B
59e2cb8199 chore: merge main into ldap branch, resolve conflicts 2026-03-29 16:28:20 +02:00
Matthieu B
6cb00478da Merge pull request #1203 from wizarrrr/fix/1121-watch-time-calculation
fix: use playback position instead of file duration for watch time
2026-03-29 16:25:47 +02:00
Matthieu B
77e738f9d7 Merge pull request #1202 from wizarrrr/fix/1195-emby-library-mapping
fix: correct Emby library ID mapping and prevent silent folder fallback (#1195)
2026-03-29 16:25:45 +02:00
Matthieu B
14df208566 Merge pull request #1201 from wizarrrr/fix/1168-plex-reinvite-update
fix: update existing Plex share on re-invite instead of failing
2026-03-29 16:25:43 +02:00
Matthieu B
73c29458fe fix: resolve all test failures, linting errors, and type errors
- Fix 55 test failures caused by missing request contexts and incorrect
  session_transaction() usage across 8 test files
- Fix ruff import sorting errors and unused imports
- Fix 122 type errors: rename method override parameters to match base
  classes, add None guards for fetchone()/datetime, widen dict type
  annotations, add type: ignore for SQLAlchemy stub limitations
- Add [tool.ty.rules] config to suppress unsupported-base warnings
- Fix _ variable shadowing gettext in wizard routes
- Add noqa: ARG002 for unused method arguments required by base class
2026-03-29 16:20:23 +02:00
Matthieu B
672475469d fix(ldap): security, architecture, and code quality improvements
- Fix SUBTREE→BASE scope in _fetch_user_attributes (security)
- Remove is_ldap_user threading from all 8 media client interfaces;
  set flag after join via workflow-level User.query.update instead
- Use find_user_dn (search-based) instead of build_user_dn (concatenation)
  in delete_user and reset_user_password for robustness
- Wrap all user-facing strings in _() for i18n in ldap_auth.py
- Fix username fallback from hardcoded "wizarr" to unique per-user
- Return failure from _update_existing_user when password update fails
- Replace dynamic Tailwind classes with explicit conditionals in alert
- Add stale group cleanup and upsert in sync_ldap_groups
- Remove fake @ldap.local email fallback in user_sync
2026-03-29 16:18:41 +02:00
Matthieu B
65ed356d62 fix: use playback position instead of file duration for watch time (#1121)
Jellyfin and Emby collectors were recording RunTimeTicks (total file
runtime) as the session duration, causing severe overestimation when
users stopped partway through long titles.

- JellyfinCollector._emit_session_event: on session_end, use position_ms
  (last known playback position) as duration_ms; fall back to
  raw_duration_ms only when position_ms is unavailable or zero.
- EmbyCollector._emit_session_event: identical fix (shared API shape).
- JellyfinHistoricalImporter._process_item: for partially-watched items
  (UserData.Played == false) use PlaybackPositionTicks; keep RunTimeTicks
  only for fully-played items where position has already been reset.
- historical_duration_source metadata field now accurately reflects
  which source was used (runtime_ticks, playback_position_ticks, or
  runtime_ticks_fallback).

Plex was already correct via elapsed-time tracking in SessionManager.
2026-03-29 16:15:56 +02:00
Matthieu B
e9f3c24581 fix: correct Emby library ID mapping and prevent silent folder fallback (#1195)
- Change EmbyClient.libraries() to return {Id: Name} instead of {Guid: Name}
  so Library.external_id stores the value Emby's EnabledFolders policy field
  expects, matching what JellyfinClient._set_specific_folders() resolves.
- Change EmbyClient.scan_libraries() to return {Name: Id} instead of
  {Name: Guid} for the same reason.
- Override _set_specific_folders() in EmbyClient with a mapping that includes
  {Name: Id}, {Id: Id}, and {Guid: Id} entries, providing backwards
  compatibility for existing Library records that still hold a Guid.
- In both EmbyClient._set_specific_folders() and the inherited
  JellyfinClient._set_specific_folders(), when the input names list is
  non-empty but all lookups fail (folder_ids ends up empty), log a structured
  WARNING via structlog and set EnableAllFolders: False with an empty
  EnabledFolders list — restricting access to nothing — rather than silently
  falling back to EnableAllFolders: True which would grant full access.
- Replace inline debug logging.info() calls with structlog structured events.
2026-03-29 16:15:37 +02:00
Matthieu B
751bfb67dd fix: update existing Plex share on re-invite instead of failing (#1168)
When a user who already has a Plex share uses a new invite link, the
"already sharing" error from the Plex API is now handled gracefully by
calling update_user_libraries() and update_user_permissions() instead of
propagating the error to the end-user. The route layer also switches to
a generic, translatable user-facing error message backed by structlog for
admin-visible diagnostics.
2026-03-29 16:15:24 +02:00
Matthieu B
498886c06a fix(ldap): address security, correctness, and code quality issues
Security:
- Fix XSS in HTMX responses by replacing f-string HTML with Jinja partials
- Fix TLS verify_cert not being applied (add Tls config to ldap3 Server)
- Fix password_hash NOT NULL conflict for LDAP-only admin accounts
- Fix should_create_ldap_user defaulting to True for existing invitations

Correctness:
- Fix DN reconstruction fragility using escape_rdn for special characters
- Fix connection leaks with try/finally in all LDAPClient methods
- Fix sync interval logic (check FLASK_ENV not WIZARR_ENABLE_SCHEDULER)
- Only register LDAP sync job when LDAP is actually configured
- Make find_user_dn and service_connection public API methods

Cleanup:
- Squash 5 incremental LDAP migrations into single 20251226_add_ldap_support
- Remove duplicate imports/queries in auth routes
- Replace f-string logging with %s formatting
- Extract inline HTML to Jinja template partials
2026-03-29 15:58:09 +02:00
Matthieu B
e992873e4d Merge origin/main into ldap branch
Resolve conflicts in auth/routes.py (incorporate media_server_url),
login.html (combine admin page text + LDAP error guard), and uv.lock.
2026-03-29 15:49:50 +02:00
Matthieu B
b0856b7473 Merge pull request #1113 from JSWANS0N/main
Add Telegram notification support
2026-03-29 15:32:36 +02:00
Matthieu B
0902f4ba93 feat(invites): streamline invite expiry handling and update UI for expiration display 2026-02-25 02:02:08 +01:00
Matthieu B
48890075f2 fix(invites): fall back to global expiry when per-server expiry is unset
When server rows exist but have no per-server expiry, the global
invitation.expires was ignored, causing expired invitations to be
treated as valid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:18:16 +01:00
Matthieu B
ad7f07319d feat(invites): implement per-server expiry handling and update invite modal closes #1138 2026-02-23 12:33:29 +01:00
lostpolaris
260165bc2a Change library deletes to upserts
This fixes a long-standing issue where scanning a library would cause an
unintended cascade delete of the invite_library table and would reset
the associated invite's permissions.
2026-02-09 17:08:30 -06:00
Jacob Swanson
7f64f558dc Add Telegram notification support
Introduces Telegram as a notification agent, including backend logic, form fields, and database columns for bot token and chat ID. Updates creation and editing flows, notification service, and UI to support Telegram integration. Includes a migration to add the new fields to the Notification model.
2026-01-23 22:03:05 -06:00
Erwan Leboucher
5bb51e0cf1 feat(ldap): add LDAP authentication and invitation integration 2025-12-27 17:14:17 +01:00
Matthieu B
89762d96dd feat: add bundle preview functionality and improve token generation tests
- Added a preview link for bundles in the wizard settings page. closes #1048
- Enhanced the wizard steps template to handle bundle preview mode.
- Updated JavaScript to generate URLs for bundle preview and runtime modes.
- Improved test coverage for password reset token generation by asserting token creation.
2025-12-09 14:37:39 +01:00
Matthieu B
ac36e793e1 refactor: improve code formatting and readability across multiple files 2025-12-09 14:12:17 +01:00
Matthieu B
0b2c90fee2 Merge pull request #1020 from 6im0n/feature/reset-password
Feat/Adds password reset functionality for Jellyfin user
2025-12-04 12:02:08 +00:00
Matthieu B
d50e0011a9 Update app/services/invites.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-23 14:47:55 +00:00
Simon
1afa37939a feat: Apply max_active_sessions to Jellyfin user policy 2025-11-21 23:50:04 +01:00
Simon
237fe620be feat: Add 'Max Active Sessions' input to invite form 2025-11-21 23:49:50 +01:00
Simon
00f3ef95c3 feat: Implement link password reset functionality 2025-11-21 00:16:24 +01:00
Simon
930b1c1a2c feat: Implements password reset functionality.
- A new API endpoint for requesting a password reset token, which can be accessed either through an API key or an authenticated session.
- A public route for handling the password reset process using the generated token.
- Updates media client to allow password resets.
2025-11-21 00:15:09 +01:00
Simon
fe57277b29 feat: Adds password reset functionality for Jellyfin defintion function 2025-11-21 00:14:29 +01:00
Matthieu B
bf223c894e Add delay to prevent hammering the media server's database during user deletion and disabling
Increase request timeout for media client API calls to  improve reliability closes #995 closes #1014
2025-11-19 15:10:29 +01:00
Matthieu B
433b0c0deb fix: update server name resolution logic for invitations to clarify priority handling 2025-11-06 13:07:55 +01:00
Matthieu B
fca67912fe feat: implement server-specific color theming for invitations and wizards closes #982 2025-11-06 12:59:57 +01:00
Matthieu B
96e2bb8e72 fix: update user fetching method to use API call during invitation acceptance
chore: add peer flag to tailwindcss license entry in package-lock.json
2025-11-06 12:21:55 +01:00
Matthieu B
da8d008398 feat: revamp activity measurement and display 2025-11-04 18:55:11 +01:00
Matthieu B
2283c4de68 fix: prevent startup race condition during migrations
This fixes a critical issue where Gunicorn workers would fail to start
after upgrading to v2025.11.0, causing containers to show as unhealthy
with only the uv wrapper process running and no actual workers.

Root Cause:
-----------
In v2025.11.0, library scanning and session recovery were added to the
create_app() function, which runs during EVERY app creation including:
1. During 'flask db upgrade' (migrations)
2. During Gunicorn master when_ready() hook
3. During each Gunicorn worker spawn

The migration 20251103_properly_fix_foreign_keys recreates 4 database
tables with CASCADE foreign keys using raw SQL. This holds exclusive
database locks during table recreation.

When library scanning and session recovery try to query these tables
during migration, they hit database locks, creating a race condition
that causes workers to timeout and crash during startup.

Fix:
----
- Skip library scanning during migrations (FLASK_SKIP_SCHEDULER=true)
- Skip activity monitoring/session recovery during migrations
- Make Gunicorn log level configurable (GUNICORN_LOG_LEVEL env var)
- Add worker lifecycle hooks for better crash debugging
- Increase healthcheck start period from 10s to 60s
- Increase Gunicorn worker timeout from 30s to 120s

Testing:
--------
- Verified app starts successfully with FLASK_SKIP_SCHEDULER=true
- Verified library scanning runs normally without the flag
- Confirmed 0.38s startup during migrations vs 1.61s normal startup

Closes #976
2025-11-03 20:41:52 +01:00
Matthieu B
af6a74d6b8 fix: improve error message handling in extract_plex_error_message and use library names in _invite_user closes #975 2025-11-03 18:35:34 +01:00
Matthieu B
d8bdc832c9 test: add assertions to ensure WizardStep retrieval after edits 2025-11-03 17:45:10 +01:00
Matthieu B
ff92ad1c54 feat: enhance Plex session management with Tautulli-inspired polling and immediate enrichment 2025-11-03 15:33:26 +01:00
Matthieu B
0b04a5f520 feat: improve user deletion process and fix foreign key constraints 2025-11-03 14:48:50 +01:00
Matthieu B
0050f4b646 feat: allow user edit 2025-11-03 14:36:04 +01:00
Matthieu B
779d2fffc7 Merge pull request #967 from DouwJacobs/fix/activity-history-filtering
Fix/activity history filtering
2025-10-30 20:19:16 +00:00
Douw Jacobs
44a654c7fc fix: change history username search to 'like' instead of exact match 2025-10-30 16:08:12 +02:00
Matthieu B
686d2625f3 feat: implement multi-level session grouping logic with extended time window and fallback matching 2025-10-28 13:08:34 +01:00
Douw Jacobs
f574120a90 fix: audiobookshelf history import order 2025-10-28 08:55:19 +02:00
Matthieu B
eca653b10b fix: kavita api errors closes #952 2025-10-27 15:04:24 +01:00