Skip to content

Commit 3f79481

Browse files
psavarmattasArakmar
authored andcommitted
feat(api): refactor fetch calls to axios
Replaces the native fetch() API with axios for all OIDC-related frontend API calls. This improves consistency with other parts of the codebase and simplifies error handling. Addresses seerr-team#1505 (review)
1 parent 6133de5 commit 3f79481

5 files changed

Lines changed: 40 additions & 45 deletions

File tree

src/components/Login/OidcLoginButton.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import defineMessages from '@app/utils/defineMessages';
22
import { processCallback } from '@app/utils/oidc';
33
import type { PublicOidcProvider } from '@server/lib/settings';
4+
import axios from 'axios';
45
import { useRouter, useSearchParams } from 'next/navigation';
56
import { useCallback, useEffect, useState } from 'react';
67
import { useIntl } from 'react-intl';
@@ -26,26 +27,19 @@ export default function OidcLoginButton({
2627
const [loading, setLoading] = useState(false);
2728

2829
const redirectToLogin = useCallback(async () => {
29-
let redirectUrl: string;
3030
try {
31-
const res = await fetch(`/api/v1/auth/oidc/login/${provider.slug}`);
32-
if (res.ok) {
33-
const data = await res.json();
34-
redirectUrl = data.redirectUrl;
35-
} else {
36-
throw new Error();
37-
}
31+
const res = await axios.get<{ redirectUrl: string }>(
32+
`/api/v1/auth/oidc/login/${provider.slug}`
33+
);
34+
window.location.href = res.data.redirectUrl;
3835
} catch (e) {
3936
setLoading(false);
4037
onError?.(
4138
intl.formatMessage(messages.oidcLoginError, {
4239
provider: provider.name,
4340
})
4441
);
45-
return;
4642
}
47-
48-
window.location.href = redirectUrl;
4943
}, [provider, intl, onError]);
5044

5145
const handleCallback = useCallback(async () => {

src/components/Settings/EditOidcModal/index.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Transition } from '@headlessui/react';
77
import { ChevronRightIcon } from '@heroicons/react/20/solid';
88
import { MagnifyingGlassIcon } from '@heroicons/react/24/solid';
99
import type { OidcProvider } from '@server/lib/settings';
10+
import axios from 'axios'; // <-- Import axios
1011
import {
1112
ErrorMessage,
1213
Field,
@@ -99,24 +100,14 @@ export default function EditOidcModal(props: EditOidcModalProps) {
99100

100101
const onSubmit = async ({ slug, ...provider }: OidcProvider) => {
101102
try {
102-
const res = await fetch(`/api/v1/settings/oidc/${slug}`, {
103-
method: 'PUT',
104-
body: JSON.stringify(provider),
105-
headers: {
106-
'Content-Type': 'application/json',
107-
},
108-
});
103+
await axios.put(`/api/v1/settings/oidc/${slug}`, provider);
109104

110-
if (res.status === 200) {
111-
addToast(intl.formatMessage(messages.saveSuccess), {
112-
appearance: 'success',
113-
autoDismiss: true,
114-
});
105+
addToast(intl.formatMessage(messages.saveSuccess), {
106+
appearance: 'success',
107+
autoDismiss: true,
108+
});
115109

116-
props.onOk();
117-
} else {
118-
throw new Error(`Request failed with code ${res.status}`);
119-
}
110+
props.onOk();
120111
} catch (e) {
121112
addToast(intl.formatMessage(messages.saveError), {
122113
appearance: 'error',

src/components/Settings/SettingsOidc/index.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Transition } from '@headlessui/react';
88
import { PlusIcon } from '@heroicons/react/24/outline';
99
import { PencilIcon, TrashIcon } from '@heroicons/react/24/solid';
1010
import type { OidcProvider, OidcSettings } from '@server/lib/settings';
11+
import axios from 'axios'; // <-- Import axios
1112
import { useState } from 'react';
1213
import { useIntl } from 'react-intl';
1314
import { useToasts } from 'react-toast-notifications';
@@ -47,14 +48,12 @@ export default function SettingsOidc(props: SettingsOidcProps) {
4748

4849
async function onDelete(provider: OidcProvider) {
4950
try {
50-
const response = await fetch(`/api/v1/settings/oidc/${provider.slug}`, {
51-
method: 'DELETE',
52-
});
53-
54-
if (response.status !== 200)
55-
throw new Error(`Request failed with status ${response.status}`);
56-
57-
revalidate(await response.json());
51+
// The fetch call is replaced with axios.delete.
52+
// Axios automatically throws for non-2xx responses and handles JSON parsing.
53+
const response = await axios.delete<OidcSettings>(
54+
`/api/v1/settings/oidc/${provider.slug}`
55+
);
56+
revalidate(response.data);
5857
} catch (e) {
5958
addToast(intl.formatMessage(messages.deleteError), {
6059
autoDismiss: true,

src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,14 @@ const UserLinkedAccountsSettings = () => {
159159
...settings.currentSettings.openIdProviders.map((p) => ({
160160
name: p.name,
161161
action: async () => {
162-
const res = await fetch(`/api/v1/auth/oidc/login/${p.slug}`);
163-
if (!res.ok) setError(intl.formatMessage(messages.errorUnknown));
164-
const json = await res.json();
165-
window.location.href = json.redirectUrl;
162+
try {
163+
const res = await axios.get<{ redirectUrl: string }>(
164+
`/api/v1/auth/oidc/login/${p.slug}`
165+
);
166+
window.location.href = res.data.redirectUrl;
167+
} catch (e) {
168+
setError(intl.formatMessage(messages.errorUnknown));
169+
}
166170
},
167171
hide: accounts.some(
168172
(a) =>

src/utils/oidc.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import axios, { isAxiosError } from 'axios'; // <-- Import axios and isAxiosError
2+
13
export async function processCallback(
24
params: URLSearchParams,
35
provider: string
@@ -9,17 +11,22 @@ export async function processCallback(
911
url.search = params.toString();
1012

1113
try {
12-
const result = await fetch(url);
13-
const message = await result.json();
14+
// The fetch call is replaced with axios.get.
15+
// On success, the JSON response is automatically parsed and available in res.data.
16+
const res = await axios.get(url.toString());
1417

15-
if (!result.ok) {
16-
return { type: 'error', message: message.message };
17-
}
1818
return {
1919
type: 'success',
20-
message,
20+
message: res.data,
2121
};
2222
} catch (e) {
23+
// Axios throws an error for non-2xx responses. We can check if it's an axios error
24+
// to safely access the error message from the server's response payload.
25+
if (isAxiosError(e) && e.response?.data?.message) {
26+
return { type: 'error', message: e.response.data.message };
27+
}
28+
29+
// Fallback for generic errors
2330
return {
2431
type: 'error',
2532
message: e.message,

0 commit comments

Comments
 (0)