refactor: switch from Axios for Fetch API (#840)

* refactor: switch ExternalAPI to Fetch API

* fix: add missing auth token in Plex request

* fix: send proper URL params

* ci: try to fix format checker

* ci: ci: try to fix format checker

* ci: try to fix format checker

* refactor: make tautulli use the ExternalAPI class

* refactor: add rate limit to fetch api

* refactor: add rate limit to fetch api

* refactor: switch server from axios to fetch api

* refactor: switch frontend from axios to fetch api

* fix: switch from URL objects to strings

* fix: use the right search params for ExternalAPI

* fix: better log for ExternalAPI errors

* feat: add retry to external API requests

* fix: try to fix network errors with IPv6

* fix: imageProxy rate limit

* revert: remove retry to external API requests

* feat: set IPv4 first as an option

* fix(jellyfinapi): add missing argument in JellyfinAPI constructor

* refactor: clean the rate limit utility
This commit is contained in:
Gauthier
2024-07-14 19:04:36 +02:00
committed by GitHub
parent ae955e9e7c
commit b36bb3fa58
100 changed files with 5380 additions and 10870 deletions

View File

@@ -14,7 +14,6 @@ import { DiscoverSliderType } from '@server/constants/discover';
import type DiscoverSlider from '@server/entity/DiscoverSlider';
import type { GenreSliderItem } from '@server/interfaces/api/discoverInterfaces';
import type { Keyword, ProductionCompany } from '@server/models/common';
import axios from 'axios';
import { Field, Form, Formik } from 'formik';
import { useCallback, useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
@@ -77,11 +76,9 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
const keywords = await Promise.all(
slider.data.split(',').map(async (keywordId) => {
const keyword = await axios.get<Keyword>(
`/api/v1/keyword/${keywordId}`
);
return keyword.data;
const res = await fetch(`/api/v1/keyword/${keywordId}`);
const keyword: Keyword = await res.json();
return keyword;
})
);
@@ -98,15 +95,13 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
return;
}
const response = await axios.get<TmdbGenre[]>(
const res = await fetch(
`/api/v1/genres/${
slider.type === DiscoverSliderType.TMDB_MOVIE_GENRE ? 'movie' : 'tv'
}`
);
const genre = response.data.find(
(genre) => genre.id === Number(slider.data)
);
const genres: TmdbGenre[] = await res.json();
const genre = genres.find((genre) => genre.id === Number(slider.data));
setDefaultDataValue([
{
@@ -121,11 +116,8 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
return;
}
const response = await axios.get<ProductionCompany>(
`/api/v1/studio/${slider.data}`
);
const studio = response.data;
const res = await fetch(`/api/v1/studio/${slider.data}`);
const studio: ProductionCompany = await res.json();
setDefaultDataValue([
{
@@ -168,16 +160,17 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
);
const loadKeywordOptions = async (inputValue: string) => {
const results = await axios.get<TmdbKeywordSearchResponse>(
'/api/v1/search/keyword',
const res = await fetch(
`/api/v1/search/keyword?query=${encodeURIExtraParams(inputValue)}`,
{
params: {
query: encodeURIExtraParams(inputValue),
headers: {
'Content-Type': 'application/json',
},
}
);
const results: TmdbKeywordSearchResponse = await res.json();
return results.data.results.map((result) => ({
return results.results.map((result) => ({
label: result.name,
value: result.id,
}));
@@ -188,38 +181,37 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
return [];
}
const results = await axios.get<TmdbCompanySearchResponse>(
'/api/v1/search/company',
const res = await fetch(
`/api/v1/search/company?query=${encodeURIExtraParams(inputValue)}`,
{
params: {
query: encodeURIExtraParams(inputValue),
headers: {
'Content-Type': 'application/json',
},
}
);
const results: TmdbCompanySearchResponse = await res.json();
return results.data.results.map((result) => ({
return results.results.map((result) => ({
label: result.name,
value: result.id,
}));
};
const loadMovieGenreOptions = async () => {
const results = await axios.get<GenreSliderItem[]>(
'/api/v1/discover/genreslider/movie'
);
const res = await fetch('/api/v1/discover/genreslider/movie');
const results: GenreSliderItem[] = await res.json();
return results.data.map((result) => ({
return results.map((result) => ({
label: result.name,
value: result.id,
}));
};
const loadTvGenreOptions = async () => {
const results = await axios.get<GenreSliderItem[]>(
'/api/v1/discover/genreslider/tv'
);
const res = await fetch('/api/v1/discover/genreslider/tv');
const results: GenreSliderItem[] = await res.json();
return results.data.map((result) => ({
return results.map((result) => ({
label: result.name,
value: result.id,
}));
@@ -314,17 +306,31 @@ const CreateSlider = ({ onCreate, slider }: CreateSliderProps) => {
onSubmit={async (values, { resetForm }) => {
try {
if (slider) {
await axios.put(`/api/v1/settings/discover/${slider.id}`, {
type: Number(values.sliderType),
title: values.title,
data: values.data,
const res = await fetch(`/api/v1/settings/discover/${slider.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: Number(values.sliderType),
title: values.title,
data: values.data,
}),
});
if (!res.ok) throw new Error();
} else {
await axios.post('/api/v1/settings/discover/add', {
type: Number(values.sliderType),
title: values.title,
data: values.data,
const res = await fetch('/api/v1/settings/discover/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: Number(values.sliderType),
title: values.title,
data: values.data,
}),
});
if (!res.ok) throw new Error();
}
addToast(

View File

@@ -20,7 +20,6 @@ import {
} from '@heroicons/react/24/solid';
import { DiscoverSliderType } from '@server/constants/discover';
import type DiscoverSlider from '@server/entity/DiscoverSlider';
import axios from 'axios';
import { useRef, useState } from 'react';
import { useDrag, useDrop } from 'react-aria';
import { useIntl } from 'react-intl';
@@ -78,7 +77,10 @@ const DiscoverSliderEdit = ({
const deleteSlider = async () => {
try {
await axios.delete(`/api/v1/settings/discover/${slider.id}`);
const res = await fetch(`/api/v1/settings/discover/${slider.id}`, {
method: 'DELETE',
});
if (!res.ok) throw new Error();
addToast(intl.formatMessage(messages.deletesuccess), {
appearance: 'success',
autoDismiss: true,

View File

@@ -28,7 +28,6 @@ import {
} from '@heroicons/react/24/solid';
import { DiscoverSliderType } from '@server/constants/discover';
import type DiscoverSlider from '@server/entity/DiscoverSlider';
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
@@ -76,7 +75,14 @@ const Discover = () => {
const updateSliders = async () => {
try {
await axios.post('/api/v1/settings/discover', sliders);
const res = await fetch('/api/v1/settings/discover', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sliders),
});
if (!res.ok) throw new Error();
addToast(intl.formatMessage(messages.updatesuccess), {
appearance: 'success',
@@ -94,7 +100,10 @@ const Discover = () => {
const resetSliders = async () => {
try {
await axios.get('/api/v1/settings/discover/reset');
const res = await fetch('/api/v1/settings/discover/reset', {
method: 'GET',
});
if (!res.ok) throw new Error();
addToast(intl.formatMessage(messages.resetsuccess), {
appearance: 'success',