mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-12-12 00:06:56 -06:00
refactor(watchlist): 🔊 Added some more logging around the watchlist authentication #5246
This commit is contained in:
parent
711f84ce63
commit
d07fade874
@ -2,6 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="RiderProjectSettingsUpdater">
|
||||
<option name="singleClickDiffPreview" value="1" />
|
||||
<option name="unhandledExceptionsIgnoreList" value="1" />
|
||||
<option name="vcsConfiguration" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
110
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
110
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
@ -4,7 +4,14 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="" />
|
||||
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/projectSettingsUpdater.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/projectSettingsUpdater.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Api.External/MediaServers/Plex/PlexApi.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Api.External/MediaServers/Plex/PlexApi.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.ts" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
@ -271,20 +278,11 @@
|
||||
</component>
|
||||
<component name="GithubPullRequestsUISettings">{
|
||||
"selectedUrlAndAccountId": {
|
||||
"url": "https://github.com/ombi-app/ombi",
|
||||
"accountId": "22dd09fe-fb9e-48a4-bfcc-3c152edf3f25"
|
||||
"url": "https://github.com/tidusjar/ombi",
|
||||
"accountId": "22715a9f-5998-4231-b224-cb858185e803"
|
||||
}
|
||||
}</component>
|
||||
<component name="HighlightingSettingsPerFile">
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/990126b794024fe2bd16aebdd37eba1d7b600/93/25662f04/ServerVersion.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7/DbContext.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/449b441523c469ed34ff5a5e14f0bafcd8f097aa463655303dc19048fa44ac3/EntityFrameworkServiceCollectionExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7d81b2d4f22bee75e5438c707251ae43cb0974c207db91ffc159118c84b4eb9/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a424e6912048b4cd25715f158e789aae24db5c2911d9e622d39bc6ac3246c6/MySqlConnectionStringBuilder.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e9881a453a581134c1a18331ac1f8f1201a5382a685bf2a40777fa22619/DbContextOptions`.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
@ -299,6 +297,7 @@
|
||||
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/db7395f4add94e6d10e515b3e55373f2821f8323de7dc8e314d78feefacf5584/ActionMethodExecutor.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
</component>
|
||||
<component name="IdeDocumentHistory">
|
||||
<option name="CHANGED_PATHS">
|
||||
@ -313,6 +312,11 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="JsbtTreeLayoutManager">
|
||||
<layout place="tools.popupGrunt">
|
||||
<scroll-view-position x="0" y="0" />
|
||||
</layout>
|
||||
</component>
|
||||
<component name="PackageJsonUpdateNotifier">
|
||||
<dismissed value="$PROJECT_DIR$/Ombi/ClientApp/package.json" />
|
||||
</component>
|
||||
@ -400,11 +404,13 @@
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"fb34c741-04ca-4b4f-8ea1-651a011b42c8.executor": "Debug",
|
||||
"git-widget-placeholder": "angular-migration-standalone",
|
||||
"git-widget-placeholder": "develop",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "yarn",
|
||||
"settings.editor.selected.configurable": "SpellingScopeSettingsId",
|
||||
"ts.external.directory.path": "/Users/tidusjar/Developer/ombi/src/Ombi/ClientApp/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
@ -497,6 +503,9 @@
|
||||
<workItem from="1745681294313" duration="1814000" />
|
||||
<workItem from="1747080279165" duration="838000" />
|
||||
<workItem from="1747082180432" duration="1994000" />
|
||||
<workItem from="1758915804840" duration="171000" />
|
||||
<workItem from="1758916150497" duration="1079000" />
|
||||
<workItem from="1759605037837" duration="2197000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
@ -565,70 +574,43 @@
|
||||
<option name="timeStamp" value="2" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
||||
<line>59</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Core/Senders/TvSender.cs</url>
|
||||
<line>123</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Senders/TvSender.cs" containingFunctionPresentation="Method 'SendToSonarr'">
|
||||
<startOffsets>
|
||||
<option value="2457" />
|
||||
<option value="4687" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2664" />
|
||||
<option value="4722" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="4" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
||||
<line>49</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'">
|
||||
<startOffsets>
|
||||
<option value="1991" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="2033" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="5" />
|
||||
<option name="timeStamp" value="17" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
|
||||
<line>110</line>
|
||||
<line>68</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
|
||||
<startOffsets>
|
||||
<option value="5211" />
|
||||
<option value="2978" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="5294" />
|
||||
<option value="3028" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="10" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
|
||||
<line>77</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
|
||||
<startOffsets>
|
||||
<option value="3412" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="3453" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="11" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
|
||||
<line>100</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
|
||||
<startOffsets>
|
||||
<option value="4690" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="4724" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="12" />
|
||||
<option name="timeStamp" value="18" />
|
||||
</line-breakpoint>
|
||||
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||
<properties exception="System.OperationCanceledException" breakIfHandledByOtherCode="false" displayValue="System.OperationCanceledException" />
|
||||
<option name="timeStamp" value="13" />
|
||||
</breakpoint>
|
||||
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||
<properties exception="System.Threading.Tasks.TaskCanceledException" breakIfHandledByOtherCode="false" displayValue="System.Threading.Tasks.TaskCanceledException" />
|
||||
<option name="timeStamp" value="14" />
|
||||
</breakpoint>
|
||||
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||
<properties exception="System.Threading.ThreadAbortException" breakIfHandledByOtherCode="false" displayValue="System.Threading.ThreadAbortException" />
|
||||
<option name="timeStamp" value="15" />
|
||||
</breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
<watches-manager>
|
||||
@ -639,6 +621,10 @@
|
||||
</configuration>
|
||||
</watches-manager>
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
<select />
|
||||
</component>
|
||||
<component name="debuggerHistoryManager">
|
||||
<expressions id="watch">
|
||||
<expression>
|
||||
|
||||
@ -321,7 +321,10 @@ namespace Ombi.Api.External.MediaServers.Plex
|
||||
|
||||
var result = await Api.Request(request, cancellationToken);
|
||||
|
||||
if (result.StatusCode.Equals(HttpStatusCode.Unauthorized))
|
||||
// Check for all possible authentication errors
|
||||
if (result.StatusCode == HttpStatusCode.Unauthorized ||
|
||||
result.StatusCode == HttpStatusCode.Forbidden ||
|
||||
result.StatusCode == HttpStatusCode.PaymentRequired)
|
||||
{
|
||||
return new PlexWatchlistContainer
|
||||
{
|
||||
@ -345,6 +348,7 @@ namespace Ombi.Api.External.MediaServers.Plex
|
||||
|
||||
/// <summary>
|
||||
/// Pings the Plex API to validate if a token is still valid
|
||||
/// Uses the same endpoint as watchlist to ensure consistent validation
|
||||
/// </summary>
|
||||
/// <param name="authToken">The authentication token to validate</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
@ -353,16 +357,23 @@ namespace Ombi.Api.External.MediaServers.Plex
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new Request("api/v2/ping", "https://plex.tv/", HttpMethod.Get);
|
||||
// Use the same endpoint as watchlist to ensure consistent token validation
|
||||
var request = new Request("library/sections/watchlist/all", WatchlistUri, HttpMethod.Get);
|
||||
await AddHeaders(request, authToken);
|
||||
|
||||
// We don't need to parse the response, just check if the request succeeds
|
||||
await Api.Request(request, cancellationToken);
|
||||
var result = await Api.Request(request, cancellationToken);
|
||||
|
||||
// Check for authentication errors (401, 403, etc.)
|
||||
if (result.StatusCode == HttpStatusCode.Unauthorized || result.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the request fails (401, 403, etc.), the token is invalid
|
||||
// If the request fails, the token is invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,23 +3,28 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.External.MediaServers.Plex;
|
||||
using Ombi.Store.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Ombi.Core.Authentication
|
||||
{
|
||||
public interface IPlexTokenKeepAliveService
|
||||
{
|
||||
Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken);
|
||||
Task<bool> TryRefreshTokenAsync(OmbiUser user, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class PlexTokenKeepAliveService : IPlexTokenKeepAliveService
|
||||
{
|
||||
private readonly IPlexApi _plexApi;
|
||||
private readonly ILogger<PlexTokenKeepAliveService> _logger;
|
||||
private readonly UserManager<OmbiUser> _userManager;
|
||||
|
||||
public PlexTokenKeepAliveService(IPlexApi plexApi, ILogger<PlexTokenKeepAliveService> logger)
|
||||
public PlexTokenKeepAliveService(IPlexApi plexApi, ILogger<PlexTokenKeepAliveService> logger, UserManager<OmbiUser> userManager)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken)
|
||||
@ -28,23 +33,63 @@ namespace Ombi.Core.Authentication
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
_logger.LogWarning("Token is null or empty");
|
||||
_logger.LogWarning("Plex token is null or empty - cannot validate");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Validating Plex token using watchlist endpoint");
|
||||
|
||||
// Use the Ping method to validate the token
|
||||
var isValid = await _plexApi.Ping(token, cancellationToken);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
_logger.LogWarning("Token validation failed - token may be expired or invalid");
|
||||
_logger.LogWarning("Plex token validation failed - token may be expired, invalid, or lacks watchlist permissions. User will need to re-authenticate.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Plex token validation successful");
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error occurred while keeping token alive");
|
||||
_logger.LogError(ex, "Unexpected error occurred while validating Plex token - treating as invalid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TryRefreshTokenAsync(OmbiUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Cannot refresh token - user is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Attempting to refresh Plex token for user '{user.UserName}'");
|
||||
|
||||
// Try to get account info with current token to see if we can get a fresh token
|
||||
var account = await _plexApi.GetAccount(user.MediaServerToken);
|
||||
|
||||
if (account?.user?.authentication_token != null &&
|
||||
account.user.authentication_token != user.MediaServerToken)
|
||||
{
|
||||
_logger.LogInformation($"Successfully refreshed Plex token for user '{user.UserName}'");
|
||||
user.MediaServerToken = account.user.authentication_token;
|
||||
await _userManager.UpdateAsync(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.LogWarning($"Could not refresh Plex token for user '{user.UserName}' - account info returned same or null token");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error occurred while trying to refresh Plex token for user '{user.UserName}'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,41 +98,58 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}");
|
||||
_logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken?.Substring(0, Math.Min(8, user.MediaServerToken?.Length ?? 0))}...");
|
||||
|
||||
// Keep the token alive before attempting watchlist import
|
||||
_logger.LogDebug($"Validating token for user '{user.UserName}' before watchlist import");
|
||||
var keepAliveSuccess = await _tokenKeepAliveService.KeepTokenAliveAsync(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
|
||||
if (!keepAliveSuccess)
|
||||
{
|
||||
_logger.LogWarning($"Token for user '{user.UserName}' is invalid or expired (keep-alive failed). Recording error and skipping.");
|
||||
await _userError.Add(new PlexWatchlistUserError
|
||||
_logger.LogWarning($"Token validation failed for user '{user.UserName}' - attempting to refresh token");
|
||||
|
||||
// Try to refresh the token before giving up
|
||||
var refreshSuccess = await _tokenKeepAliveService.TryRefreshTokenAsync(user, context?.CancellationToken ?? CancellationToken.None);
|
||||
if (refreshSuccess)
|
||||
{
|
||||
UserId = user.Id,
|
||||
MediaServerToken = user.MediaServerToken,
|
||||
});
|
||||
|
||||
// Send notification to user about token expiration
|
||||
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
|
||||
{
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
NotificationType = NotificationType.PlexWatchlistTokenExpired,
|
||||
Recipient = user.Email,
|
||||
DateTime = DateTime.Now,
|
||||
Substitutes = new Dictionary<string, string>
|
||||
{
|
||||
{ "UserName", user.UserName }
|
||||
}
|
||||
};
|
||||
await _notificationHelper.Notify(notificationModel);
|
||||
_logger.LogInformation($"Successfully refreshed token for user '{user.UserName}', retrying watchlist import");
|
||||
// Re-validate the refreshed token
|
||||
keepAliveSuccess = await _tokenKeepAliveService.KeepTokenAliveAsync(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
|
||||
}
|
||||
|
||||
if (!keepAliveSuccess)
|
||||
{
|
||||
_logger.LogWarning($"Token validation and refresh failed for user '{user.UserName}' - token may be expired, invalid, or lacks watchlist permissions. Recording error and skipping user.");
|
||||
await _userError.Add(new PlexWatchlistUserError
|
||||
{
|
||||
UserId = user.Id,
|
||||
MediaServerToken = user.MediaServerToken,
|
||||
});
|
||||
|
||||
// Send notification to user about token expiration
|
||||
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
|
||||
{
|
||||
_logger.LogInformation($"Sending token expiration notification to user '{user.UserName}' at {user.Email}");
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
NotificationType = NotificationType.PlexWatchlistTokenExpired,
|
||||
Recipient = user.Email,
|
||||
DateTime = DateTime.Now,
|
||||
Substitutes = new Dictionary<string, string>
|
||||
{
|
||||
{ "UserName", user.UserName }
|
||||
}
|
||||
};
|
||||
await _notificationHelper.Notify(notificationModel);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Token validation successful for user '{user.UserName}', proceeding with watchlist import");
|
||||
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
|
||||
if (watchlist?.AuthError ?? false)
|
||||
{
|
||||
_logger.LogError($"Auth failed for user '{user.UserName}'. Need to re-authenticate with Ombi.");
|
||||
_logger.LogError($"Authentication error occurred during watchlist API call for user '{user.UserName}' - this should not happen after successful token validation. User needs to re-authenticate.");
|
||||
await _userError.Add(new PlexWatchlistUserError
|
||||
{
|
||||
UserId = user.Id,
|
||||
@ -142,6 +159,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||
// Send notification to user about token expiration
|
||||
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
|
||||
{
|
||||
_logger.LogInformation($"Sending token expiration notification to user '{user.UserName}' at {user.Email}");
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
NotificationType = NotificationType.PlexWatchlistTokenExpired,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { MatTableDataSource } from "@angular/material/table";
|
||||
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
|
||||
import { take } from "rxjs";
|
||||
import { IPlexWatchlistUsers, WatchlistSyncStatus } from "../../../../interfaces";
|
||||
import { PlexService } from "../../../../services";
|
||||
@ -7,6 +7,7 @@ import { MatButtonModule } from "@angular/material/button";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
import { MatCardModule } from "@angular/material/card";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { MatDialogModule } from "@angular/material/dialog";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@ -17,6 +18,8 @@ import { CommonModule } from "@angular/common";
|
||||
MatIconModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
MatTableModule
|
||||
]
|
||||
})
|
||||
export class PlexWatchlistComponent implements OnInit{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user