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 {};