From 5c0d5262d50709c1ab6aad6917958e46d325e457 Mon Sep 17 00:00:00 2001
From: Gauthier
Date: Tue, 25 Mar 2025 14:45:52 +0100
Subject: [PATCH] fix: rewrite error handling for Axios and remove IPv4 first
setting
---
cypress/config/settings.cypress.json | 3 +-
jellyseerr-api.yml | 3 -
server/api/jellyfin.ts | 78 ++++++------
server/api/servarr/radarr.ts | 9 +-
server/api/servarr/sonarr.ts | 9 +-
server/index.ts | 15 ---
server/lib/notifications/agents/discord.ts | 9 +-
server/lib/notifications/agents/gotify.ts | 9 +-
server/lib/notifications/agents/lunasea.ts | 9 +-
server/lib/notifications/agents/pushover.ts | 9 +-
server/lib/notifications/agents/slack.ts | 9 +-
server/lib/notifications/agents/telegram.ts | 27 +----
server/lib/notifications/agents/webhook.ts | 9 +-
server/lib/settings/index.ts | 2 -
server/utils/customProxyAgent.ts | 113 ------------------
server/utils/restartFlag.ts | 3 +-
src/components/Blacklist/index.tsx | 6 +-
src/components/BlacklistBlock/index.tsx | 6 +-
src/components/MovieDetails/index.tsx | 72 ++++++-----
.../OverrideRule/OverrideRuleTiles.tsx | 2 +-
src/components/Settings/SettingsJellyfin.tsx | 20 +---
.../Settings/SettingsNetwork/index.tsx | 28 -----
src/components/TitleCard/index.tsx | 47 ++++----
src/components/TvDetails/index.tsx | 70 ++++++-----
src/components/UserList/index.tsx | 9 +-
.../UserGeneralSettings/index.tsx | 9 +-
.../UserNotificationsWebPush/index.tsx | 74 ++++--------
src/i18n/locale/en.json | 2 -
src/pages/_app.tsx | 9 +-
src/utils/fetchOverride.ts | 46 -------
30 files changed, 188 insertions(+), 528 deletions(-)
delete mode 100644 server/utils/customProxyAgent.ts
delete mode 100644 src/utils/fetchOverride.ts
diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json
index c466b2bf..30190736 100644
--- a/cypress/config/settings.cypress.json
+++ b/cypress/config/settings.cypress.json
@@ -23,7 +23,6 @@
"mediaServerType": 1,
"partialRequestsEnabled": true,
"enableSpecialEpisodes": false,
- "forceIpv4First": false,
"locale": "en"
},
"plex": {
@@ -179,4 +178,4 @@
"schedule": "0 0 5 * * *"
}
}
-}
+}
\ No newline at end of file
diff --git a/jellyseerr-api.yml b/jellyseerr-api.yml
index 6954992d..b427bcf8 100644
--- a/jellyseerr-api.yml
+++ b/jellyseerr-api.yml
@@ -191,9 +191,6 @@ components:
csrfProtection:
type: boolean
example: false
- forceIpv4First:
- type: boolean
- example: false
trustProxy:
type: boolean
example: true
diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts
index 6b965b8c..b09c8186 100644
--- a/server/api/jellyfin.ts
+++ b/server/api/jellyfin.ts
@@ -163,36 +163,36 @@ class JellyfinAPI extends ExternalAPI {
} catch (e) {
logger.debug('Failed to authenticate with headers', {
label: 'Jellyfin API',
- error: e.cause.message ?? e.cause.statusText,
+ error: e.response?.status,
ip: ClientIP,
});
- if (!e.cause.status) {
+ if (!e.response?.status) {
throw new ApiError(404, ApiErrorCode.InvalidUrl);
}
- if (e.cause.status === 401) {
- throw new ApiError(e.cause.status, ApiErrorCode.InvalidCredentials);
+ if (e.response?.status === 401) {
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidCredentials);
}
}
try {
return await authenticate(false);
} catch (e) {
- if (e.cause.status === 401) {
- throw new ApiError(e.cause.status, ApiErrorCode.InvalidCredentials);
+ if (e.response?.status === 401) {
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidCredentials);
}
logger.error(
- 'Something went wrong while authenticating with the Jellyfin server',
+ `Something went wrong while authenticating with the Jellyfin server: ${e.message}`,
{
label: 'Jellyfin API',
- error: e.cause.message ?? e.cause.statusText,
+ error: e.response?.status,
ip: ClientIP,
}
);
- throw new ApiError(e.cause.status, ApiErrorCode.Unknown);
+ throw new ApiError(e.response?.status, ApiErrorCode.Unknown);
}
}
@@ -207,7 +207,7 @@ class JellyfinAPI extends ExternalAPI {
return systemInfoResponse;
} catch (e) {
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -220,11 +220,11 @@ class JellyfinAPI extends ExternalAPI {
return serverResponse.ServerName;
} catch (e) {
logger.error(
- 'Something went wrong while getting the server name from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting the server name from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.Unknown);
+ throw new ApiError(e.response?.status, ApiErrorCode.Unknown);
}
}
@@ -235,11 +235,11 @@ class JellyfinAPI extends ExternalAPI {
return { users: userReponse };
} catch (e) {
logger.error(
- 'Something went wrong while getting the account from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting the account from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -251,11 +251,11 @@ class JellyfinAPI extends ExternalAPI {
return userReponse;
} catch (e) {
logger.error(
- 'Something went wrong while getting the account from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting the account from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -275,10 +275,10 @@ class JellyfinAPI extends ExternalAPI {
return this.mapLibraries(mediaFolderResponse.Items);
} catch (e) {
logger.error(
- 'Something went wrong while getting libraries from the Jellyfin server',
+ `Something went wrong while getting libraries from the Jellyfin server: ${e.message}`,
{
label: 'Jellyfin API',
- error: e.cause.message ?? e.cause.statusText,
+ error: e.response?.status,
}
);
@@ -324,11 +324,11 @@ class JellyfinAPI extends ExternalAPI {
);
} catch (e) {
logger.error(
- 'Something went wrong while getting library content from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting library content from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e?.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -349,11 +349,11 @@ class JellyfinAPI extends ExternalAPI {
return itemResponse;
} catch (e) {
logger.error(
- 'Something went wrong while getting library content from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting library content from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -371,16 +371,16 @@ class JellyfinAPI extends ExternalAPI {
return itemResponse.Items?.[0];
} catch (e) {
if (availabilitySync.running) {
- if (e.cause?.status === 500) {
+ if (e.response?.status === 500) {
return undefined;
}
}
logger.error(
- 'Something went wrong while getting library content from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting library content from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -391,11 +391,11 @@ class JellyfinAPI extends ExternalAPI {
return seasonResponse.Items;
} catch (e) {
logger.error(
- 'Something went wrong while getting the list of seasons from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting the list of seasons from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -413,11 +413,11 @@ class JellyfinAPI extends ExternalAPI {
);
} catch (e) {
logger.error(
- 'Something went wrong while getting the list of episodes from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while getting the list of episodes from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
- throw new ApiError(e.cause?.status, ApiErrorCode.InvalidAuthToken);
+ throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
}
}
@@ -430,8 +430,8 @@ class JellyfinAPI extends ExternalAPI {
).AccessToken;
} catch (e) {
logger.error(
- 'Something went wrong while creating an API key from the Jellyfin server',
- { label: 'Jellyfin API', error: e.cause.message ?? e.cause.statusText }
+ `Something went wrong while creating an API key from the Jellyfin server: ${e.message}`,
+ { label: 'Jellyfin API', error: e.response?.status }
);
throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken);
diff --git a/server/api/servarr/radarr.ts b/server/api/servarr/radarr.ts
index ceb3f10d..e11aca9a 100644
--- a/server/api/servarr/radarr.ts
+++ b/server/api/servarr/radarr.ts
@@ -214,20 +214,13 @@ class RadarrAPI extends ServarrBase<{ movieId: number }> {
}
return response.data;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error(
'Failed to add movie to Radarr. This might happen if the movie already exists, in which case you can safely ignore this error.',
{
label: 'Radarr',
errorMessage: e.message,
options,
- response: errorData,
+ response: e?.response?.data,
}
);
throw new Error('Failed to add movie to Radarr');
diff --git a/server/api/servarr/sonarr.ts b/server/api/servarr/sonarr.ts
index f9199f4c..ab8e837e 100644
--- a/server/api/servarr/sonarr.ts
+++ b/server/api/servarr/sonarr.ts
@@ -266,18 +266,11 @@ class SonarrAPI extends ServarrBase<{
return createdSeriesResponse.data;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Something went wrong while adding a series to Sonarr.', {
label: 'Sonarr API',
errorMessage: e.message,
options,
- response: errorData,
+ response: e?.response?.data,
});
throw new Error('Failed to add series');
}
diff --git a/server/index.ts b/server/index.ts
index 31fdfc99..506dfcec 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -24,7 +24,6 @@ import avatarproxy from '@server/routes/avatarproxy';
import imageproxy from '@server/routes/imageproxy';
import { appDataPermissions } from '@server/utils/appDataVolume';
import { getAppVersion } from '@server/utils/appVersion';
-// import createCustomProxyAgent from '@server/utils/customProxyAgent';
import restartFlag from '@server/utils/restartFlag';
import { getClientIp } from '@supercharge/request-ip';
import { TypeormStore } from 'connect-typeorm/out';
@@ -72,20 +71,6 @@ app
const settings = await getSettings().load();
restartFlag.initializeSettings(settings);
- // // Check if we force IPv4 first
- // if (
- // process.env.forceIpv4First === 'true' ||
- // settings.network.forceIpv4First
- // ) {
- // dns.setDefaultResultOrder('ipv4first');
- // net.setDefaultAutoSelectFamily(false);
- // }
-
- // // Register HTTP proxy
- // if (settings.network.proxy.enabled) {
- // await createCustomProxyAgent(settings.network.proxy);
- // }
-
// Migrate library types
if (
settings.plex.libraries.length > 1 &&
diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts
index 6cd22395..cabd332d 100644
--- a/server/lib/notifications/agents/discord.ts
+++ b/server/lib/notifications/agents/discord.ts
@@ -309,19 +309,12 @@ class DiscordAgent
return true;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Discord notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/notifications/agents/gotify.ts b/server/lib/notifications/agents/gotify.ts
index c36fc453..514281f7 100644
--- a/server/lib/notifications/agents/gotify.ts
+++ b/server/lib/notifications/agents/gotify.ts
@@ -144,19 +144,12 @@ class GotifyAgent
return true;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Gotify notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/notifications/agents/lunasea.ts b/server/lib/notifications/agents/lunasea.ts
index 05bd48de..acfa7df9 100644
--- a/server/lib/notifications/agents/lunasea.ts
+++ b/server/lib/notifications/agents/lunasea.ts
@@ -117,19 +117,12 @@ class LunaSeaAgent
return true;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending LunaSea notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts
index dfce656f..7abf0d72 100644
--- a/server/lib/notifications/agents/pushover.ts
+++ b/server/lib/notifications/agents/pushover.ts
@@ -65,17 +65,10 @@ class PushoverAgent
attachment_type: contentType,
};
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error getting image payload', {
label: 'Notifications',
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return {};
}
diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts
index d24e554c..8f1f0c95 100644
--- a/server/lib/notifications/agents/slack.ts
+++ b/server/lib/notifications/agents/slack.ts
@@ -245,19 +245,12 @@ class SlackAgent
return true;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Slack notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts
index 580ed576..10d4bce4 100644
--- a/server/lib/notifications/agents/telegram.ts
+++ b/server/lib/notifications/agents/telegram.ts
@@ -183,19 +183,12 @@ class TelegramAgent
disable_notification: !!settings.options.sendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Telegram notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
@@ -226,20 +219,13 @@ class TelegramAgent
!!payload.notifyUser.settings.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Telegram notification', {
label: 'Notifications',
recipient: payload.notifyUser.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
@@ -279,20 +265,13 @@ class TelegramAgent
disable_notification: !!user.settings?.telegramSendSilently,
} as TelegramMessagePayload | TelegramPhotoPayload);
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending Telegram notification', {
label: 'Notifications',
recipient: user.displayName,
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts
index 7d948db7..c441cb65 100644
--- a/server/lib/notifications/agents/webhook.ts
+++ b/server/lib/notifications/agents/webhook.ts
@@ -192,19 +192,12 @@ class WebhookAgent
return true;
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
logger.error('Error sending webhook notification', {
label: 'Notifications',
type: Notification[type],
subject: payload.subject,
errorMessage: e.message,
- response: errorData,
+ response: e?.response?.data,
});
return false;
diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts
index 3630c42e..a4738d65 100644
--- a/server/lib/settings/index.ts
+++ b/server/lib/settings/index.ts
@@ -136,7 +136,6 @@ export interface MainSettings {
export interface NetworkSettings {
csrfProtection: boolean;
- forceIpv4First: boolean;
trustProxy: boolean;
proxy: ProxySettings;
}
@@ -510,7 +509,6 @@ class Settings {
network: {
csrfProtection: false,
trustProxy: false,
- forceIpv4First: false,
proxy: {
enabled: false,
hostname: '',
diff --git a/server/utils/customProxyAgent.ts b/server/utils/customProxyAgent.ts
deleted file mode 100644
index 5f163c3d..00000000
--- a/server/utils/customProxyAgent.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import type { ProxySettings } from '@server/lib/settings';
-import logger from '@server/logger';
-import type { Dispatcher } from 'undici';
-import { Agent, ProxyAgent, setGlobalDispatcher } from 'undici';
-
-export default async function createCustomProxyAgent(
- proxySettings: ProxySettings
-) {
- const defaultAgent = new Agent({ keepAliveTimeout: 5000 });
-
- const skipUrl = (url: string | URL) => {
- const hostname =
- typeof url === 'string' ? new URL(url).hostname : url.hostname;
-
- if (proxySettings.bypassLocalAddresses && isLocalAddress(hostname)) {
- return true;
- }
-
- for (const address of proxySettings.bypassFilter.split(',')) {
- const trimmedAddress = address.trim();
- if (!trimmedAddress) {
- continue;
- }
-
- if (trimmedAddress.startsWith('*')) {
- const domain = trimmedAddress.slice(1);
- if (hostname.endsWith(domain)) {
- return true;
- }
- } else if (hostname === trimmedAddress) {
- return true;
- }
- }
-
- return false;
- };
-
- const noProxyInterceptor = (
- dispatch: Dispatcher['dispatch']
- ): Dispatcher['dispatch'] => {
- return (opts, handler) => {
- return opts.origin && skipUrl(opts.origin)
- ? defaultAgent.dispatch(opts, handler)
- : dispatch(opts, handler);
- };
- };
-
- const token =
- proxySettings.user && proxySettings.password
- ? `Basic ${Buffer.from(
- `${proxySettings.user}:${proxySettings.password}`
- ).toString('base64')}`
- : undefined;
-
- try {
- const proxyAgent = new ProxyAgent({
- uri:
- (proxySettings.useSsl ? 'https://' : 'http://') +
- proxySettings.hostname +
- ':' +
- proxySettings.port,
- token,
- keepAliveTimeout: 5000,
- });
-
- setGlobalDispatcher(proxyAgent.compose(noProxyInterceptor));
- } catch (e) {
- logger.error('Failed to connect to the proxy: ' + e.message, {
- label: 'Proxy',
- });
- setGlobalDispatcher(defaultAgent);
- return;
- }
-
- try {
- const res = await fetch('https://www.google.com', { method: 'HEAD' });
- if (res.ok) {
- logger.debug('HTTP(S) proxy connected successfully', { label: 'Proxy' });
- } else {
- logger.error('Proxy responded, but with a non-OK status: ' + res.status, {
- label: 'Proxy',
- });
- setGlobalDispatcher(defaultAgent);
- }
- } catch (e) {
- logger.error(
- 'Failed to connect to the proxy: ' + e.message + ': ' + e.cause,
- { label: 'Proxy' }
- );
- setGlobalDispatcher(defaultAgent);
- }
-}
-
-function isLocalAddress(hostname: string) {
- if (
- hostname === 'localhost' ||
- hostname === '127.0.0.1' ||
- hostname === '::1'
- ) {
- return true;
- }
-
- const privateIpRanges = [
- /^10\./, // 10.x.x.x
- /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.x.x - 172.31.x.x
- /^192\.168\./, // 192.168.x.x
- ];
- if (privateIpRanges.some((regex) => regex.test(hostname))) {
- return true;
- }
-
- return false;
-}
diff --git a/server/utils/restartFlag.ts b/server/utils/restartFlag.ts
index ffd64df3..d0a492ba 100644
--- a/server/utils/restartFlag.ts
+++ b/server/utils/restartFlag.ts
@@ -17,8 +17,7 @@ class RestartFlag {
return (
this.networkSettings.csrfProtection !== networkSettings.csrfProtection ||
this.networkSettings.trustProxy !== networkSettings.trustProxy ||
- this.networkSettings.proxy.enabled !== networkSettings.proxy.enabled ||
- this.networkSettings.forceIpv4First !== networkSettings.forceIpv4First
+ this.networkSettings.proxy.enabled !== networkSettings.proxy.enabled
);
}
}
diff --git a/src/components/Blacklist/index.tsx b/src/components/Blacklist/index.tsx
index 4d4eb0b4..5c83465f 100644
--- a/src/components/Blacklist/index.tsx
+++ b/src/components/Blacklist/index.tsx
@@ -239,9 +239,9 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => {
const removeFromBlacklist = async (tmdbId: number, title?: string) => {
setIsUpdating(true);
- const res = await axios.delete(`/api/v1/blacklist/${tmdbId}`);
+ try {
+ await axios.delete(`/api/v1/blacklist/${tmdbId}`);
- if (res.status === 204) {
addToast(
{intl.formatMessage(globalMessages.removeFromBlacklistSuccess, {
@@ -251,7 +251,7 @@ const BlacklistedItem = ({ item, revalidateList }: BlacklistedItemProps) => {
,
{ appearance: 'success', autoDismiss: true }
);
- } else {
+ } catch {
addToast(intl.formatMessage(globalMessages.blacklistError), {
appearance: 'error',
autoDismiss: true,
diff --git a/src/components/BlacklistBlock/index.tsx b/src/components/BlacklistBlock/index.tsx
index d7aa3347..6980a02e 100644
--- a/src/components/BlacklistBlock/index.tsx
+++ b/src/components/BlacklistBlock/index.tsx
@@ -39,9 +39,9 @@ const BlacklistBlock = ({
const removeFromBlacklist = async (tmdbId: number, title?: string) => {
setIsUpdating(true);
- const res = await axios.delete('/api/v1/blacklist/' + tmdbId);
+ try {
+ await axios.delete('/api/v1/blacklist/' + tmdbId);
- if (res.status === 204) {
addToast(
{intl.formatMessage(globalMessages.removeFromBlacklistSuccess, {
@@ -51,7 +51,7 @@ const BlacklistBlock = ({
,
{ appearance: 'success', autoDismiss: true }
);
- } else {
+ } catch {
addToast(intl.formatMessage(globalMessages.blacklistError), {
appearance: 'error',
autoDismiss: true,
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx
index 97bb4174..29b3ab56 100644
--- a/src/components/MovieDetails/index.tsx
+++ b/src/components/MovieDetails/index.tsx
@@ -327,9 +327,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
title: movie?.title,
});
- const data = await response.json();
-
- if (data) {
+ if (response.data) {
addToast(
{intl.formatMessage(messages.watchlistSuccess, {
@@ -354,19 +352,17 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
const onClickDeleteWatchlistBtn = async (): Promise => {
setIsUpdating(true);
try {
- const res = await axios.delete(`/api/v1/watchlist/${movie?.id}`);
+ await axios.delete(`/api/v1/watchlist/${movie?.id}`);
- if (res.status === 204) {
- addToast(
-
- {intl.formatMessage(messages.watchlistDeleted, {
- title: movie?.title,
- strong: (msg: React.ReactNode) => {msg},
- })}
- ,
- { appearance: 'info', autoDismiss: true }
- );
- }
+ addToast(
+
+ {intl.formatMessage(messages.watchlistDeleted, {
+ title: movie?.title,
+ strong: (msg: React.ReactNode) => {msg},
+ })}
+ ,
+ { appearance: 'info', autoDismiss: true }
+ );
} catch (e) {
addToast(intl.formatMessage(messages.watchlistError), {
appearance: 'error',
@@ -381,14 +377,14 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
const onClickHideItemBtn = async (): Promise => {
setIsBlacklistUpdating(true);
- const res = await axios.post('/api/v1/blacklist', {
- tmdbId: movie?.id,
- mediaType: 'movie',
- title: movie?.title,
- user: user?.id,
- });
+ try {
+ await axios.post('/api/v1/blacklist', {
+ tmdbId: movie?.id,
+ mediaType: 'movie',
+ title: movie?.title,
+ user: user?.id,
+ });
- if (res.status === 201) {
addToast(
{intl.formatMessage(globalMessages.blacklistSuccess, {
@@ -400,21 +396,23 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
);
revalidate();
- } else if (res.status === 412) {
- addToast(
-
- {intl.formatMessage(globalMessages.blacklistDuplicateError, {
- title: movie?.title,
- strong: (msg: React.ReactNode) => {msg},
- })}
- ,
- { appearance: 'info', autoDismiss: true }
- );
- } else {
- addToast(intl.formatMessage(globalMessages.blacklistError), {
- appearance: 'error',
- autoDismiss: true,
- });
+ } catch (e) {
+ if (e?.response?.status === 412) {
+ addToast(
+
+ {intl.formatMessage(globalMessages.blacklistDuplicateError, {
+ title: movie?.title,
+ strong: (msg: React.ReactNode) => {msg},
+ })}
+ ,
+ { appearance: 'info', autoDismiss: true }
+ );
+ } else {
+ addToast(intl.formatMessage(globalMessages.blacklistError), {
+ appearance: 'error',
+ autoDismiss: true,
+ });
+ }
}
setIsBlacklistUpdating(false);
diff --git a/src/components/Settings/OverrideRule/OverrideRuleTiles.tsx b/src/components/Settings/OverrideRule/OverrideRuleTiles.tsx
index e1457c05..4208836f 100644
--- a/src/components/Settings/OverrideRule/OverrideRuleTiles.tsx
+++ b/src/components/Settings/OverrideRule/OverrideRuleTiles.tsx
@@ -65,7 +65,7 @@ const OverrideRuleTiles = ({
for (const service of services) {
const { hostname, port, apiKey, baseUrl, useSsl = false } = service;
try {
- const response = await axios.post(
+ const response = await axios.post(
`/api/v1/settings/${
radarrServices.includes(service as RadarrSettings)
? 'radarr'
diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx
index 25636f1c..e66519f0 100644
--- a/src/components/Settings/SettingsJellyfin.tsx
+++ b/src/components/Settings/SettingsJellyfin.tsx
@@ -185,14 +185,7 @@ const SettingsJellyfin: React.FC = ({
setIsSyncing(false);
revalidate();
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
- if (errorData?.message === 'SYNC_ERROR_GROUPED_FOLDERS') {
+ if (e?.response?.data?.message === 'SYNC_ERROR_GROUPED_FOLDERS') {
toasts.addToast(
intl.formatMessage(
messages.jellyfinSyncFailedAutomaticGroupedFolders
@@ -202,7 +195,7 @@ const SettingsJellyfin: React.FC = ({
appearance: 'warning',
}
);
- } else if (errorData?.message === 'SYNC_ERROR_NO_LIBRARIES') {
+ } else if (e?.response?.data?.message === 'SYNC_ERROR_NO_LIBRARIES') {
toasts.addToast(
intl.formatMessage(messages.jellyfinSyncFailedNoLibrariesFound),
{
@@ -488,14 +481,7 @@ const SettingsJellyfin: React.FC = ({
}
);
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
- if (errorData?.message === ApiErrorCode.InvalidUrl) {
+ if (e?.response?.data?.message === ApiErrorCode.InvalidUrl) {
addToast(
intl.formatMessage(
messages.invalidurlerror,
diff --git a/src/components/Settings/SettingsNetwork/index.tsx b/src/components/Settings/SettingsNetwork/index.tsx
index ce661851..27dad900 100644
--- a/src/components/Settings/SettingsNetwork/index.tsx
+++ b/src/components/Settings/SettingsNetwork/index.tsx
@@ -43,9 +43,6 @@ const messages = defineMessages('components.Settings.SettingsNetwork', {
networkDisclaimer:
'Network parameters from your container/system should be used instead of these settings. See the {docs} for more information.',
docs: 'documentation',
- forceIpv4First: 'Force IPv4 Resolution First',
- forceIpv4FirstTip:
- 'Force Jellyseerr to resolve IPv4 addresses first instead of IPv6',
});
const SettingsNetwork = () => {
@@ -90,7 +87,6 @@ const SettingsNetwork = () => {
{
try {
await axios.post('/api/v1/settings/network', {
csrfProtection: values.csrfProtection,
- forceIpv4First: values.forceIpv4First,
trustProxy: values.trustProxy,
proxy: {
enabled: values.proxyEnabled,
@@ -392,29 +387,6 @@ const SettingsNetwork = () => {
),
})}
-
-
-
- {
- setFieldValue('forceIpv4First', !values.forceIpv4First);
- }}
- />
-
-
diff --git a/src/components/TitleCard/index.tsx b/src/components/TitleCard/index.tsx
index 1b04826e..865e8239 100644
--- a/src/components/TitleCard/index.tsx
+++ b/src/components/TitleCard/index.tsx
@@ -173,14 +173,13 @@ const TitleCard = ({
const topNode = cardRef.current;
if (topNode) {
- const res = await axios.post('/api/v1/blacklist', {
- tmdbId: id,
- mediaType,
- title,
- user: user?.id,
- });
-
- if (res.status === 201) {
+ try {
+ await axios.post('/api/v1/blacklist', {
+ tmdbId: id,
+ mediaType,
+ title,
+ user: user?.id,
+ });
addToast(
{intl.formatMessage(globalMessages.blacklistSuccess, {
@@ -191,21 +190,23 @@ const TitleCard = ({
{ appearance: 'success', autoDismiss: true }
);
setCurrentStatus(MediaStatus.BLACKLISTED);
- } else if (res.status === 412) {
- addToast(
-
- {intl.formatMessage(globalMessages.blacklistDuplicateError, {
- title,
- strong: (msg: React.ReactNode) => {msg},
- })}
- ,
- { appearance: 'info', autoDismiss: true }
- );
- } else {
- addToast(intl.formatMessage(globalMessages.blacklistError), {
- appearance: 'error',
- autoDismiss: true,
- });
+ } catch (e) {
+ if (e?.response?.status === 412) {
+ addToast(
+
+ {intl.formatMessage(globalMessages.blacklistDuplicateError, {
+ title,
+ strong: (msg: React.ReactNode) => {msg},
+ })}
+ ,
+ { appearance: 'info', autoDismiss: true }
+ );
+ } else {
+ addToast(intl.formatMessage(globalMessages.blacklistError), {
+ appearance: 'error',
+ autoDismiss: true,
+ });
+ }
}
setIsUpdating(false);
diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx
index c9ea1c7e..cf85aef1 100644
--- a/src/components/TvDetails/index.tsx
+++ b/src/components/TvDetails/index.tsx
@@ -411,40 +411,44 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
const onClickHideItemBtn = async (): Promise => {
setIsBlacklistUpdating(true);
- const res = await axios.post('/api/v1/blacklist', {
- tmdbId: tv?.id,
- mediaType: 'tv',
- title: tv?.name,
- user: user?.id,
- });
-
- if (res.status === 201) {
- addToast(
-
- {intl.formatMessage(globalMessages.blacklistSuccess, {
- title: tv?.name,
- strong: (msg: React.ReactNode) => {msg},
- })}
- ,
- { appearance: 'success', autoDismiss: true }
- );
-
- revalidate();
- } else if (res.status === 412) {
- addToast(
-
- {intl.formatMessage(globalMessages.blacklistDuplicateError, {
- title: tv?.name,
- strong: (msg: React.ReactNode) => {msg},
- })}
- ,
- { appearance: 'info', autoDismiss: true }
- );
- } else {
- addToast(intl.formatMessage(globalMessages.blacklistError), {
- appearance: 'error',
- autoDismiss: true,
+ try {
+ const res = await axios.post('/api/v1/blacklist', {
+ tmdbId: tv?.id,
+ mediaType: 'tv',
+ title: tv?.name,
+ user: user?.id,
});
+
+ if (res.status === 201) {
+ addToast(
+
+ {intl.formatMessage(globalMessages.blacklistSuccess, {
+ title: tv?.name,
+ strong: (msg: React.ReactNode) => {msg},
+ })}
+ ,
+ { appearance: 'success', autoDismiss: true }
+ );
+
+ revalidate();
+ }
+ } catch (e) {
+ if (e?.response?.status === 412) {
+ addToast(
+
+ {intl.formatMessage(globalMessages.blacklistDuplicateError, {
+ title: tv?.name,
+ strong: (msg: React.ReactNode) => {msg},
+ })}
+ ,
+ { appearance: 'info', autoDismiss: true }
+ );
+ } else {
+ addToast(intl.formatMessage(globalMessages.blacklistError), {
+ appearance: 'error',
+ autoDismiss: true,
+ });
+ }
}
setIsBlacklistUpdating(false);
diff --git a/src/components/UserList/index.tsx b/src/components/UserList/index.tsx
index 5e0976b0..877f95ae 100644
--- a/src/components/UserList/index.tsx
+++ b/src/components/UserList/index.tsx
@@ -295,16 +295,9 @@ const UserList = () => {
});
setCreateModal({ isOpen: false });
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
addToast(
intl.formatMessage(
- errorData.errors?.includes('USER_EXISTS')
+ e?.response?.data?.errors?.includes('USER_EXISTS')
? messages.usercreatedfailedexisting
: messages.usercreatedfailed
),
diff --git a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
index 46be83a6..44e422e9 100644
--- a/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserGeneralSettings/index.tsx
@@ -197,14 +197,7 @@ const UserGeneralSettings = () => {
appearance: 'success',
});
} catch (e) {
- let errorData;
- try {
- errorData = await e.cause?.text();
- errorData = JSON.parse(errorData);
- } catch {
- /* empty */
- }
- if (errorData?.message === ApiErrorCode.InvalidEmail) {
+ if (e?.response?.data?.message === ApiErrorCode.InvalidEmail) {
if (values.email) {
addToast(
intl.formatMessage(messages.toastSettingsFailureEmail),
diff --git a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
index de438e3a..feb54214 100644
--- a/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
+++ b/src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
@@ -16,6 +16,7 @@ import {
} from '@heroicons/react/24/solid';
import type { UserPushSubscription } from '@server/entity/UserPushSubscription';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
+import axios from 'axios';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
@@ -84,21 +85,12 @@ const UserWebPushSettings = () => {
const parsedSub = JSON.parse(JSON.stringify(sub));
if (parsedSub.keys.p256dh && parsedSub.keys.auth) {
- const res = await fetch('/api/v1/user/registerPushSubscription', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- endpoint: parsedSub.endpoint,
- p256dh: parsedSub.keys.p256dh,
- auth: parsedSub.keys.auth,
- userAgent: navigator.userAgent,
- }),
+ await axios.post('/api/v1/user/registerPushSubscription', {
+ endpoint: parsedSub.endpoint,
+ p256dh: parsedSub.keys.p256dh,
+ auth: parsedSub.keys.auth,
+ userAgent: navigator.userAgent,
});
- if (!res.ok) {
- throw new Error(res.statusText);
- }
setWebPushEnabled(true);
addToast(intl.formatMessage(messages.webpushhasbeenenabled), {
appearance: 'success',
@@ -129,17 +121,11 @@ const UserWebPushSettings = () => {
.then(async (subscription) => {
const parsedSub = JSON.parse(JSON.stringify(subscription));
- const res = await fetch(
+ await axios.delete(
`/api/v1/user/${user?.id}/pushSubscription/${
p256dh ? p256dh : parsedSub.keys.p256dh
- }`,
- {
- method: 'DELETE',
- }
+ }`
);
- if (!res.ok) {
- throw new Error(res.statusText);
- }
if (subscription && (p256dh === parsedSub.keys.p256dh || !p256dh)) {
subscription.unsubscribe();
setWebPushEnabled(false);
@@ -188,17 +174,10 @@ const UserWebPushSettings = () => {
.then(async (subscription) => {
if (subscription) {
const parsedKey = JSON.parse(JSON.stringify(subscription));
- const response = await fetch(
- `/api/v1/user/${user.id}/pushSubscription/${parsedKey.keys.p256dh}`
- );
-
- if (!response.ok) {
- throw new Error(response.statusText);
- }
-
- const currentUserPushSub = {
- data: (await response.json()) as UserPushSubscription,
- };
+ const currentUserPushSub =
+ await axios.get(
+ `/api/v1/user/${user.id}/pushSubscription/${parsedKey.keys.p256dh}`
+ );
if (currentUserPushSub.data.p256dh !== parsedKey.keys.p256dh) {
return;
@@ -233,30 +212,21 @@ const UserWebPushSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
- const res = await fetch(
+ await axios.post(
`/api/v1/user/${user?.id}/settings/notifications`,
{
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
+ pgpKey: data?.pgpKey,
+ discordId: data?.discordId,
+ pushbulletAccessToken: data?.pushbulletAccessToken,
+ pushoverApplicationToken: data?.pushoverApplicationToken,
+ pushoverUserKey: data?.pushoverUserKey,
+ telegramChatId: data?.telegramChatId,
+ telegramSendSilently: data?.telegramSendSilently,
+ notificationTypes: {
+ webpush: values.types,
},
- body: JSON.stringify({
- pgpKey: data?.pgpKey,
- discordId: data?.discordId,
- pushbulletAccessToken: data?.pushbulletAccessToken,
- pushoverApplicationToken: data?.pushoverApplicationToken,
- pushoverUserKey: data?.pushoverUserKey,
- telegramChatId: data?.telegramChatId,
- telegramSendSilently: data?.telegramSendSilently,
- notificationTypes: {
- webpush: values.types,
- },
- }),
}
);
- if (!res.ok) {
- throw new Error(res.statusText);
- }
mutate('/api/v1/settings/public');
addToast(intl.formatMessage(messages.webpushsettingssaved), {
appearance: 'success',
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json
index d4269b96..1ade9ff2 100644
--- a/src/i18n/locale/en.json
+++ b/src/i18n/locale/en.json
@@ -943,8 +943,6 @@
"components.Settings.SettingsNetwork.csrfProtectionHoverTip": "Do NOT enable this setting unless you understand what you are doing!",
"components.Settings.SettingsNetwork.csrfProtectionTip": "Set external API access to read-only (requires HTTPS)",
"components.Settings.SettingsNetwork.docs": "documentation",
- "components.Settings.SettingsNetwork.forceIpv4First": "Force IPv4 Resolution First",
- "components.Settings.SettingsNetwork.forceIpv4FirstTip": "Force Jellyseerr to resolve IPv4 addresses first instead of IPv6",
"components.Settings.SettingsNetwork.network": "Network",
"components.Settings.SettingsNetwork.networkDisclaimer": "Network parameters from your container/system should be used instead of these settings. See the {docs} for more information.",
"components.Settings.SettingsNetwork.networksettings": "Network Settings",
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index ec25cb50..d79a7904 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -13,7 +13,6 @@ import { UserContext } from '@app/context/UserContext';
import type { User } from '@app/hooks/useUser';
import { Permission, useUser } from '@app/hooks/useUser';
import '@app/styles/globals.css';
-import '@app/utils/fetchOverride';
import { polyfillIntl } from '@app/utils/polyfillIntl';
import { MediaServerType } from '@server/constants/server';
import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces';
@@ -239,7 +238,9 @@ CoreApp.getInitialProps = async (initialProps) => {
if (ctx.res) {
// Check if app is initialized and redirect if necessary
const response = await axios.get(
- `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 5055}/api/v1/settings/public`
+ `http://${process.env.HOST || 'localhost'}:${
+ process.env.PORT || 5055
+ }/api/v1/settings/public`
);
currentSettings = response.data;
@@ -257,7 +258,9 @@ CoreApp.getInitialProps = async (initialProps) => {
try {
// Attempt to get the user by running a request to the local api
const response = await axios.get(
- `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 5055}/api/v1/auth/me`,
+ `http://${process.env.HOST || 'localhost'}:${
+ process.env.PORT || 5055
+ }/api/v1/auth/me`,
{
headers:
ctx.req && ctx.req.headers.cookie
diff --git a/src/utils/fetchOverride.ts b/src/utils/fetchOverride.ts
deleted file mode 100644
index e0a90012..00000000
--- a/src/utils/fetchOverride.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-const getCsrfToken = (): string | null => {
- if (typeof window !== 'undefined') {
- const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
- return match ? decodeURIComponent(match[1]) : null;
- }
- return null;
-};
-
-const isSameOrigin = (url: RequestInfo | URL): boolean => {
- const parsedUrl = new URL(
- url instanceof Request ? url.url : url.toString(),
- window.location.origin
- );
- return parsedUrl.origin === window.location.origin;
-};
-
-// We are using a custom fetch implementation to add the X-XSRF-TOKEN heade
-// to all requests. This is required when CSRF protection is enabled.
-if (typeof window !== 'undefined') {
- const originalFetch: typeof fetch = window.fetch;
-
- (window as typeof globalThis).fetch = async (
- input: RequestInfo | URL,
- init?: RequestInit
- ): Promise => {
- if (!isSameOrigin(input)) {
- return originalFetch(input, init);
- }
-
- const csrfToken = getCsrfToken();
-
- const headers = {
- ...(init?.headers || {}),
- ...(csrfToken ? { 'XSRF-TOKEN': csrfToken } : {}),
- };
-
- const newInit: RequestInit = {
- ...init,
- headers,
- };
-
- return originalFetch(input, newInit);
- };
-}
-
-export {};