refactor: switch from Fetch API to Axios (#1520)

* refactor: switch from Fetch API to Axios

* fix: remove unwanted changes

* fix: rewrite error handling for Axios and remove IPv4 first setting

* style: run prettier

* style: run prettier

* fix: add back custom proxy agent

* fix: add back custom proxy agent

* fix: correct rebase issue

* fix: resolve review comments
This commit is contained in:
Gauthier
2025-04-08 13:20:10 +02:00
committed by GitHub
parent 21400cecdc
commit a488f850f3
112 changed files with 1654 additions and 3032 deletions

View File

@@ -16,6 +16,7 @@ import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import { ApiErrorCode } from '@server/constants/error';
import type { UserSettingsGeneralResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
@@ -164,33 +165,24 @@ const UserGeneralSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(`/api/v1/user/${user?.id}/settings/main`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: values.displayName,
email:
values.email || user?.jellyfinUsername || user?.plexUsername,
discordId: values.discordId,
locale: values.locale,
discoverRegion: values.discoverRegion,
streamingRegion: values.streamingRegion,
originalLanguage: values.originalLanguage,
movieQuotaLimit: movieQuotaEnabled
? values.movieQuotaLimit
: null,
movieQuotaDays: movieQuotaEnabled
? values.movieQuotaDays
: null,
tvQuotaLimit: tvQuotaEnabled ? values.tvQuotaLimit : null,
tvQuotaDays: tvQuotaEnabled ? values.tvQuotaDays : null,
watchlistSyncMovies: values.watchlistSyncMovies,
watchlistSyncTv: values.watchlistSyncTv,
}),
await axios.post(`/api/v1/user/${user?.id}/settings/main`, {
username: values.displayName,
email:
values.email || user?.jellyfinUsername || user?.plexUsername,
discordId: values.discordId,
locale: values.locale,
discoverRegion: values.discoverRegion,
streamingRegion: values.streamingRegion,
originalLanguage: values.originalLanguage,
movieQuotaLimit: movieQuotaEnabled
? values.movieQuotaLimit
: null,
movieQuotaDays: movieQuotaEnabled ? values.movieQuotaDays : null,
tvQuotaLimit: tvQuotaEnabled ? values.tvQuotaLimit : null,
tvQuotaDays: tvQuotaEnabled ? values.tvQuotaDays : null,
watchlistSyncMovies: values.watchlistSyncMovies,
watchlistSyncTv: values.watchlistSyncTv,
});
if (!res.ok) throw new Error(res.statusText, { cause: res });
if (currentUser?.id === user?.id && setLocale) {
setLocale(
@@ -205,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),

View File

@@ -5,6 +5,7 @@ import { useUser } from '@app/hooks/useUser';
import defineMessages from '@app/utils/defineMessages';
import { Transition } from '@headlessui/react';
import { MediaServerType } from '@server/constants/server';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useState } from 'react';
import { useIntl } from 'react-intl';
@@ -80,38 +81,28 @@ const LinkJellyfinModal: React.FC<LinkJellyfinModalProps> = ({
onSubmit={async ({ username, password }) => {
try {
setError(null);
const res = await fetch(
await axios.post(
`/api/v1/user/${user?.id}/settings/linked-accounts/jellyfin`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
password,
}),
username,
password,
}
);
if (!res.ok) {
if (res.status === 401) {
setError(
intl.formatMessage(messages.errorUnauthorized, {
mediaServerName,
})
);
} else if (res.status === 422) {
setError(
intl.formatMessage(messages.errorExists, { applicationName })
);
} else {
setError(intl.formatMessage(messages.errorUnknown));
}
} else {
onSave();
}
onSave();
} catch (e) {
setError(intl.formatMessage(messages.errorUnknown));
if (e?.response?.status === 401) {
setError(
intl.formatMessage(messages.errorUnauthorized, {
mediaServerName,
})
);
} else if (e?.response?.status === 422) {
setError(
intl.formatMessage(messages.errorExists, { applicationName })
);
} else {
setError(intl.formatMessage(messages.errorUnknown));
}
}
}}
>

View File

@@ -12,6 +12,7 @@ import defineMessages from '@app/utils/defineMessages';
import PlexOAuth from '@app/utils/plex';
import { TrashIcon } from '@heroicons/react/24/solid';
import { MediaServerType } from '@server/constants/server';
import axios from 'axios';
import { useRouter } from 'next/router';
import { useMemo, useState } from 'react';
import { useIntl } from 'react-intl';
@@ -91,25 +92,21 @@ const UserLinkedAccountsSettings = () => {
setError(null);
try {
const authToken = await plexOAuth.login();
const res = await fetch(
await axios.post(
`/api/v1/user/${user?.id}/settings/linked-accounts/plex`,
{
method: 'POST',
body: JSON.stringify({ authToken }),
authToken,
}
);
if (!res.ok) {
if (res.status === 401) {
setError(intl.formatMessage(messages.plexErrorUnauthorized));
} else if (res.status === 422) {
setError(intl.formatMessage(messages.plexErrorExists));
} else {
setError(intl.formatMessage(messages.errorUnknown));
}
} else {
await revalidateUser();
}
await revalidateUser();
} catch (e) {
if (e?.response?.status === 401) {
setError(intl.formatMessage(messages.plexErrorUnauthorized));
} else if (e?.response?.status === 422) {
setError(intl.formatMessage(messages.plexErrorExists));
} else {
setError(intl.formatMessage(messages.errorUnknown));
}
setError(intl.formatMessage(messages.errorUnknown));
}
};
@@ -143,11 +140,9 @@ const UserLinkedAccountsSettings = () => {
const deleteRequest = async (account: string) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/linked-accounts/${account}`,
{ method: 'DELETE' }
await axios.delete(
`/api/v1/user/${user?.id}/settings/linked-accounts/${account}`
);
if (!res.ok) throw new Error();
} catch {
setError(intl.formatMessage(messages.deleteFailed));
}

View File

@@ -6,6 +6,7 @@ import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -67,28 +68,18 @@ const UserNotificationsDiscord = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/notifications`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pgpKey: data?.pgpKey,
discordId: values.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
discord: values.types,
},
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/notifications`, {
pgpKey: data?.pgpKey,
discordId: values.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
discord: values.types,
},
});
addToast(intl.formatMessage(messages.discordsettingssaved), {
appearance: 'success',
autoDismiss: true,

View File

@@ -11,6 +11,7 @@ import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -66,28 +67,18 @@ const UserEmailSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/notifications`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pgpKey: values.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
email: values.types,
},
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/notifications`, {
pgpKey: values.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
email: values.types,
},
});
addToast(intl.formatMessage(messages.emailsettingssaved), {
appearance: 'success',
autoDismiss: true,

View File

@@ -6,6 +6,7 @@ import { useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -64,28 +65,18 @@ const UserPushbulletSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/notifications`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: values.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
pushbullet: values.types,
},
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/notifications`, {
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: values.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
pushbullet: values.types,
},
});
addToast(intl.formatMessage(messages.pushbulletsettingssaved), {
appearance: 'success',
autoDismiss: true,

View File

@@ -7,6 +7,7 @@ import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import type { PushoverSound } from '@server/api/pushover';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -96,28 +97,18 @@ const UserPushoverSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/notifications`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: values.pushoverApplicationToken,
pushoverUserKey: values.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
pushover: values.types,
},
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/notifications`, {
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: values.pushoverApplicationToken,
pushoverUserKey: values.pushoverUserKey,
telegramChatId: data?.telegramChatId,
telegramSendSilently: data?.telegramSendSilently,
notificationTypes: {
pushover: values.types,
},
});
addToast(intl.formatMessage(messages.pushoversettingssaved), {
appearance: 'success',
autoDismiss: true,

View File

@@ -6,6 +6,7 @@ import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import type { UserSettingsNotificationsResponse } from '@server/interfaces/api/userSettingsInterfaces';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -90,29 +91,19 @@ const UserTelegramSettings = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/notifications`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: values.telegramChatId,
telegramMessageThreadId: values.telegramMessageThreadId,
telegramSendSilently: values.telegramSendSilently,
notificationTypes: {
telegram: values.types,
},
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/notifications`, {
pgpKey: data?.pgpKey,
discordId: data?.discordId,
pushbulletAccessToken: data?.pushbulletAccessToken,
pushoverApplicationToken: data?.pushoverApplicationToken,
pushoverUserKey: data?.pushoverUserKey,
telegramChatId: values.telegramChatId,
telegramMessageThreadId: values.telegramMessageThreadId,
telegramSendSilently: values.telegramSendSilently,
notificationTypes: {
telegram: values.types,
},
});
addToast(intl.formatMessage(messages.telegramsettingssaved), {
appearance: 'success',
autoDismiss: true,

View File

@@ -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<UserPushSubscription>(
`/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',

View File

@@ -8,6 +8,7 @@ import globalMessages from '@app/i18n/globalMessages';
import ErrorPage from '@app/pages/_error';
import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import axios from 'axios';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -122,21 +123,11 @@ const UserPasswordChange = () => {
enableReinitialize
onSubmit={async (values, { resetForm }) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/password`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
currentPassword: values.currentPassword,
newPassword: values.newPassword,
confirmPassword: values.confirmPassword,
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/password`, {
currentPassword: values.currentPassword,
newPassword: values.newPassword,
confirmPassword: values.confirmPassword,
});
addToast(intl.formatMessage(messages.toastSettingsSuccess), {
autoDismiss: true,

View File

@@ -8,6 +8,7 @@ import globalMessages from '@app/i18n/globalMessages';
import ErrorPage from '@app/pages/_error';
import defineMessages from '@app/utils/defineMessages';
import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import axios from 'axios';
import { Form, Formik } from 'formik';
import { useRouter } from 'next/router';
import { useIntl } from 'react-intl';
@@ -83,19 +84,10 @@ const UserPermissions = () => {
enableReinitialize
onSubmit={async (values) => {
try {
const res = await fetch(
`/api/v1/user/${user?.id}/settings/permissions`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
permissions: values.currentPermissions ?? 0,
}),
}
);
if (!res.ok) throw new Error();
await axios.post(`/api/v1/user/${user?.id}/settings/permissions`, {
permissions: values.currentPermissions ?? 0,
});
addToast(intl.formatMessage(messages.toastSettingsSuccess), {
autoDismiss: true,
appearance: 'success',