From 0dbec64680a2c5a6f5e8a12b1f14104751f35392 Mon Sep 17 00:00:00 2001 From: rtrembecky Date: Mon, 16 Dec 2024 17:12:03 +0100 Subject: [PATCH] rework API access to support local and deployed BE --- .env | 21 +++++++++++- next.config.ts | 10 ------ package.json | 2 +- src/api/apiAxios.ts | 29 ++++++++++++++++ src/api/apiInterceptor.ts | 25 ++++++++++++++ src/components/Admin/dataProvider.ts | 25 ++++++++------ src/components/Admin/useAuthProvider.ts | 2 +- src/components/Archive/Archive.tsx | 4 +-- .../CompetitionPage/CompetitionPage.tsx | 4 +-- src/components/FileUploader/FileUploader.tsx | 5 +-- src/components/PageLayout/Footer/Footer.tsx | 6 ++-- .../PageLayout/MenuMain/MenuMain.tsx | 4 +-- .../PasswordResetRequest.tsx | 4 +-- .../PasswordReset/PasswordReset.tsx | 4 +-- src/components/Posts/Posts.tsx | 4 +-- .../ProblemAdministration.tsx | 16 ++++----- src/components/Problems/Discussion.tsx | 14 ++++---- src/components/Problems/Problems.tsx | 10 +++--- src/components/Problems/UploadProblemForm.tsx | 5 +-- src/components/Profile/PasswordChangeForm.tsx | 5 +-- src/components/Profile/ProfileDetail.tsx | 4 +-- src/components/Profile/ProfileForm.tsx | 6 ++-- src/components/RegisterForm/RegisterForm.tsx | 5 +-- src/components/Results/Results.tsx | 6 ++-- .../SchoolSubForm/SchoolSubForm.tsx | 6 ++-- .../SemesterAdministration.tsx | 11 +++--- src/components/VerifyEmail/VerifyEmail.tsx | 5 +-- src/pages/strom/[page].tsx | 6 ++-- src/pages/strom/akcie/[[...params]].tsx | 6 ++-- src/utils/AuthContainer.tsx | 34 ++++++------------- src/utils/urlBase.ts | 12 +++++++ src/utils/useDataFromURL.tsx | 6 ++-- src/utils/useHasPermissions.ts | 4 +-- yarn.lock | 10 +++--- 34 files changed, 195 insertions(+), 125 deletions(-) create mode 100644 src/api/apiAxios.ts create mode 100644 src/api/apiInterceptor.ts create mode 100644 src/utils/urlBase.ts diff --git a/.env b/.env index 4e852c89..a8e516a7 100644 --- a/.env +++ b/.env @@ -1,3 +1,22 @@ +## pre server-side requesty +BE_PROTOCOL=http +BE_HOSTNAME=localhost +BE_PORT=8000 + +## localhost:8000 NEXT_PUBLIC_BE_PROTOCOL=http NEXT_PUBLIC_BE_HOSTNAME=localhost -NEXT_PUBLIC_BE_PORT=8000 \ No newline at end of file +NEXT_PUBLIC_BE_PORT=8000 +NEXT_PUBLIC_BE_PREFIX= + +## test.strom.sk +# NEXT_PUBLIC_BE_PROTOCOL=https +# NEXT_PUBLIC_BE_HOSTNAME=test.strom.sk +# NEXT_PUBLIC_BE_PORT= +# NEXT_PUBLIC_BE_PREFIX=/api + +## strom.sk +# NEXT_PUBLIC_BE_PROTOCOL=https +# NEXT_PUBLIC_BE_HOSTNAME=strom.sk +# NEXT_PUBLIC_BE_PORT= +# NEXT_PUBLIC_BE_PREFIX=/api \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index 11930d0a..aa30e56c 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,16 +1,6 @@ import type {NextConfig} from 'next' const nextConfig: NextConfig = { - // docs: https://nextjs.org/docs/api-reference/next.config.js/rewrites - async rewrites() { - return [ - // rewrite API requestov na django BE (podstatne aj koncove lomitko) - { - source: '/api/:path*', - destination: `${process.env.NEXT_PUBLIC_BE_PROTOCOL}://${process.env.NEXT_PUBLIC_BE_HOSTNAME}:${process.env.NEXT_PUBLIC_BE_PORT}/:path*/`, - }, - ] - }, images: { remotePatterns: [ { diff --git a/package.json b/package.json index 0ec8acab..784bb27d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^14.3.1", "@testing-library/user-event": "^14.5.2", - "axios": "^1.7.7", + "axios": "^1.7.9", "clsx": "^2.1.1", "katex": "^0.16.11", "luxon": "^3.5.0", diff --git a/src/api/apiAxios.ts b/src/api/apiAxios.ts new file mode 100644 index 00000000..b4916426 --- /dev/null +++ b/src/api/apiAxios.ts @@ -0,0 +1,29 @@ +import axios from 'axios' + +import {apiInterceptor} from '@/api/apiInterceptor' +import {getInternalServerUrl, getPublicServerUrl} from '@/utils/urlBase' + +export const newApiAxios = (base: 'server' | 'client') => { + const baseUrl = base === 'server' ? getInternalServerUrl() : getPublicServerUrl() + + const instance = axios.create({ + baseURL: baseUrl, + // auth pozostava z comba: + // 1. `sessionid` httpOnly cookie - nastavuje a maze su server pri login/logout + // 2. CSRF hlavicka - server nastavuje cookie, ktorej hodnotu treba vlozit do hlavicky. axios riesi automaticky podla tohto configu + withXSRFToken: true, + xsrfCookieName: 'csrftoken', + xsrfHeaderName: 'X-CSRFToken', + // bez tohto sa neposielaju v requeste cookies + withCredentials: true, + }) + + instance.interceptors.request.use(apiInterceptor) + + return instance +} + +// nasa "globalna" API instancia +export const apiAxios = newApiAxios('client') + +export const serverApiAxios = newApiAxios('server') diff --git a/src/api/apiInterceptor.ts b/src/api/apiInterceptor.ts new file mode 100644 index 00000000..b2c95f9b --- /dev/null +++ b/src/api/apiInterceptor.ts @@ -0,0 +1,25 @@ +import axios from 'axios' + +type RequestInterceptor = Parameters[0] + +export const apiInterceptor: RequestInterceptor = (config) => { + if (config.url?.startsWith('/')) { + const [pathname, search] = config.url.split('?') + let newPathname = pathname + + // nahrad prefix "/api" (ked existuje) za iny prefix (lokalne "", na deployed BE "/api") + newPathname = newPathname.replace(/^\/api/, process.env.NEXT_PUBLIC_BE_PREFIX ?? '') + + // BE Django server ocakava trailing slash (aj pred query params). + // priklady: + // - /api/cms/post -> /api/cms/post/ + // - /api/cms/post?search=textik -> /api/cms/post/?search=textik + if (!newPathname.endsWith('/')) { + newPathname = `${newPathname}/` + } + + config.url = `${newPathname}${search ? `?${search}` : ''}` + } + + return config +} diff --git a/src/components/Admin/dataProvider.ts b/src/components/Admin/dataProvider.ts index 61a13a82..d5c4dc68 100644 --- a/src/components/Admin/dataProvider.ts +++ b/src/components/Admin/dataProvider.ts @@ -1,7 +1,9 @@ -import axios, {isAxiosError} from 'axios' +import {isAxiosError} from 'axios' import {stringify} from 'querystring' import {DataProvider, FilterPayload, /* PaginationPayload, */ SortPayload} from 'react-admin' +import {apiAxios} from '@/api/apiAxios' + const getFilterQuery = ({q, ...otherSearchParams}: FilterPayload) => ({ ...otherSearchParams, search: q, @@ -48,7 +50,10 @@ export const dataProvider: DataProvider = { const stringifiedQuery = stringify(query) try { - const {data} = await axios.get(`${apiUrl}/${resource}${stringifiedQuery ? `/?${stringifiedQuery}` : ''}`) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const {data} = await apiAxios.get( + `${apiUrl}/${resource}${stringifiedQuery ? `/?${stringifiedQuery}` : ''}`, + ) // client-side pagination let pagedData = data @@ -67,7 +72,7 @@ export const dataProvider: DataProvider = { }, getOne: async (resource, params) => { try { - const {data} = await axios.get(`${apiUrl}/${resource}/${params.id}`) + const {data} = await apiAxios.get(`${apiUrl}/${resource}/${params.id}`) return {data} } catch (e) { throw new Error(parseError(e)) @@ -75,7 +80,7 @@ export const dataProvider: DataProvider = { }, getMany: async (resource, params) => { try { - const data = await Promise.all(params.ids.map((id) => axios.get(`${apiUrl}/${resource}/${id}`))) + const data = await Promise.all(params.ids.map((id) => apiAxios.get(`${apiUrl}/${resource}/${id}`))) return {data: data.map(({data}) => data)} } catch (e) { throw new Error(parseError(e)) @@ -88,7 +93,7 @@ export const dataProvider: DataProvider = { } try { - const {data} = await axios.get(`${apiUrl}/${resource}/?${stringify(query)}`) + const {data} = await apiAxios.get(`${apiUrl}/${resource}/?${stringify(query)}`) return { data: data, total: data.length, @@ -105,7 +110,7 @@ export const dataProvider: DataProvider = { const body = formData ?? input try { - const {data} = await axios.patch(`${apiUrl}/${resource}/${id}`, body) + const {data} = await apiAxios.patch(`${apiUrl}/${resource}/${id}`, body) return {data} } catch (e) { throw new Error(parseError(e)) @@ -113,7 +118,7 @@ export const dataProvider: DataProvider = { }, updateMany: async (resource, params) => { try { - const data = await Promise.all(params.ids.map((id) => axios.patch(`${apiUrl}/${resource}/${id}`, params.data))) + const data = await Promise.all(params.ids.map((id) => apiAxios.patch(`${apiUrl}/${resource}/${id}`, params.data))) return {data: data.map(({data}) => data)} } catch (e) { throw new Error(parseError(e)) @@ -125,7 +130,7 @@ export const dataProvider: DataProvider = { const body = formData ?? input try { - const {data} = await axios.post(`${apiUrl}/${resource}`, body) + const {data} = await apiAxios.post(`${apiUrl}/${resource}`, body) return {data} } catch (e) { throw new Error(parseError(e)) @@ -133,7 +138,7 @@ export const dataProvider: DataProvider = { }, delete: async (resource, params) => { try { - const {data} = await axios.delete(`${apiUrl}/${resource}/${params.id}`) + const {data} = await apiAxios.delete(`${apiUrl}/${resource}/${params.id}`) return {data} } catch (e) { throw new Error(parseError(e)) @@ -141,7 +146,7 @@ export const dataProvider: DataProvider = { }, deleteMany: async (resource, params) => { try { - const data = await Promise.all(params.ids.map((id) => axios.delete(`${apiUrl}/${resource}/${id}`))) + const data = await Promise.all(params.ids.map((id) => apiAxios.delete(`${apiUrl}/${resource}/${id}`))) return {data: data.map(({data}) => data.id)} } catch (e) { throw new Error(parseError(e)) diff --git a/src/components/Admin/useAuthProvider.ts b/src/components/Admin/useAuthProvider.ts index b9d7d617..4a90110f 100644 --- a/src/components/Admin/useAuthProvider.ts +++ b/src/components/Admin/useAuthProvider.ts @@ -19,7 +19,7 @@ export const useAuthProvider = () => { await testAuthRequest() }, checkError: async (error) => { - // rovnaky handling ako v `responseIntercepto`r v `AuthContainer` + // rovnaky handling ako v `responseInterceptor` v `AuthContainer` const status = error.response?.status if (status === 403) { diff --git a/src/components/Archive/Archive.tsx b/src/components/Archive/Archive.tsx index 525cb76a..168013f7 100644 --- a/src/components/Archive/Archive.tsx +++ b/src/components/Archive/Archive.tsx @@ -1,8 +1,8 @@ import {Stack, Typography} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' import {FC} from 'react' +import {apiAxios} from '@/api/apiAxios' import {Event, Publication} from '@/types/api/competition' import {useSeminarInfo} from '@/utils/useSeminarInfo' @@ -60,7 +60,7 @@ export const Archive: FC = () => { const {data: eventListData, isLoading: eventListIsLoading} = useQuery({ queryKey: ['competition', 'event', `competition=${seminarId}`], - queryFn: () => axios.get(`/api/competition/event/?competition=${seminarId}`), + queryFn: () => apiAxios.get(`/api/competition/event/?competition=${seminarId}`), }) const eventList = eventListData?.data ?? [] diff --git a/src/components/CompetitionPage/CompetitionPage.tsx b/src/components/CompetitionPage/CompetitionPage.tsx index 0f24cee8..2b80635f 100644 --- a/src/components/CompetitionPage/CompetitionPage.tsx +++ b/src/components/CompetitionPage/CompetitionPage.tsx @@ -1,7 +1,7 @@ import {Stack, Typography} from '@mui/material' import Grid from '@mui/material/Unstable_Grid2' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useRouter} from 'next/router' import {FC, Fragment, useEffect} from 'react' @@ -33,7 +33,7 @@ export const CompetitionPage: FC = ({ const {data: bannerMessage, isLoading: isBannerLoading} = useQuery({ queryKey: ['cms', 'info-banner', 'competition', id], - queryFn: () => axios.get(`/api/cms/info-banner/competition/${id}`), + queryFn: () => apiAxios.get(`/api/cms/info-banner/competition/${id}`), enabled: id !== -1, }) diff --git a/src/components/FileUploader/FileUploader.tsx b/src/components/FileUploader/FileUploader.tsx index 52b617af..b98e16cc 100644 --- a/src/components/FileUploader/FileUploader.tsx +++ b/src/components/FileUploader/FileUploader.tsx @@ -1,9 +1,10 @@ import {Upload} from '@mui/icons-material' import {useMutation} from '@tanstack/react-query' -import axios from 'axios' import {FC, useCallback} from 'react' import {Accept, DropzoneOptions, useDropzone} from 'react-dropzone' +import {apiAxios} from '@/api/apiAxios' + interface FileUploaderProps { uploadLink: string acceptedFormats?: Accept @@ -13,7 +14,7 @@ interface FileUploaderProps { export const FileUploader: FC = ({uploadLink, acceptedFormats, adjustFormData, refetch}) => { const {mutate: fileUpload} = useMutation({ - mutationFn: (formData: FormData) => axios.post(uploadLink, formData), + mutationFn: (formData: FormData) => apiAxios.post(uploadLink, formData), onSuccess: () => refetch(), }) diff --git a/src/components/PageLayout/Footer/Footer.tsx b/src/components/PageLayout/Footer/Footer.tsx index 6d0c037c..c3d43280 100644 --- a/src/components/PageLayout/Footer/Footer.tsx +++ b/src/components/PageLayout/Footer/Footer.tsx @@ -1,7 +1,7 @@ import {Stack} from '@mui/material' import Grid from '@mui/material/Unstable_Grid2' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {FC} from 'react' import {Link} from '@/components/Clickable/Link' @@ -19,7 +19,7 @@ export const Footer: FC = () => { error: menuItemsError, } = useQuery({ queryKey: ['cms', 'menu-item', 'on-site', seminarId, '?footer'], - queryFn: () => axios.get(`/api/cms/menu-item/on-site/${seminarId}?type=footer`), + queryFn: () => apiAxios.get(`/api/cms/menu-item/on-site/${seminarId}?type=footer`), }) const menuItems = menuItemsData?.data ?? [] @@ -29,7 +29,7 @@ export const Footer: FC = () => { error: logosError, } = useQuery({ queryKey: ['cms', 'logo'], - queryFn: () => axios.get('/api/cms/logo'), + queryFn: () => apiAxios.get('/api/cms/logo'), }) const logos = logosData?.data.filter((logo) => !logo.disabled) ?? [] diff --git a/src/components/PageLayout/MenuMain/MenuMain.tsx b/src/components/PageLayout/MenuMain/MenuMain.tsx index aa6b52a4..aed54700 100644 --- a/src/components/PageLayout/MenuMain/MenuMain.tsx +++ b/src/components/PageLayout/MenuMain/MenuMain.tsx @@ -1,9 +1,9 @@ import {Box, Drawer, Stack, Theme, useMediaQuery} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' import {useRouter} from 'next/router' import {FC, useEffect, useState} from 'react' +import {apiAxios} from '@/api/apiAxios' import {Link} from '@/components/Clickable/Link' import {CloseButton} from '@/components/CloseButton/CloseButton' import {Loading} from '@/components/Loading/Loading' @@ -43,7 +43,7 @@ export const MenuMain: FC = () => { const {data: menuItemsData, isLoading: menuItemsIsLoading} = useQuery({ queryKey: ['cms', 'menu-item', 'on-site', seminarId, '?menu'], - queryFn: () => axios.get(`/api/cms/menu-item/on-site/${seminarId}?type=menu`), + queryFn: () => apiAxios.get(`/api/cms/menu-item/on-site/${seminarId}?type=menu`), }) const menuItems = menuItemsData?.data ?? [] diff --git a/src/components/PageLayout/PasswordResetRequest/PasswordResetRequest.tsx b/src/components/PageLayout/PasswordResetRequest/PasswordResetRequest.tsx index 7040aa9a..eaf3ae04 100644 --- a/src/components/PageLayout/PasswordResetRequest/PasswordResetRequest.tsx +++ b/src/components/PageLayout/PasswordResetRequest/PasswordResetRequest.tsx @@ -1,6 +1,6 @@ import {Stack} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {FC} from 'react' import {SubmitHandler, useForm} from 'react-hook-form' @@ -27,7 +27,7 @@ export const PasswordResetRequestForm: FC = ({cl const {mutate: submitFormData} = useMutation({ mutationFn: (data: PasswordResetRequestFormValues) => { - return axios.post('/api/user/password/reset', data) + return apiAxios.post('/api/user/password/reset', data) }, onSuccess: () => { diff --git a/src/components/PasswordReset/PasswordReset.tsx b/src/components/PasswordReset/PasswordReset.tsx index 01860aa5..6684235e 100644 --- a/src/components/PasswordReset/PasswordReset.tsx +++ b/src/components/PasswordReset/PasswordReset.tsx @@ -1,6 +1,6 @@ import {Stack, Typography} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useRouter} from 'next/router' import {FC} from 'react' import {SubmitHandler, useForm} from 'react-hook-form' @@ -46,7 +46,7 @@ export const PasswordResetForm: FC = ({uid, token}) => { const {mutate: submitFormData, isSuccess: isReset} = useMutation({ mutationFn: (data: PasswordResetForm) => { - return axios.post('/api/user/password/reset/confirm', transformFormData(data)) + return apiAxios.post('/api/user/password/reset/confirm', transformFormData(data)) }, }) diff --git a/src/components/Posts/Posts.tsx b/src/components/Posts/Posts.tsx index 590ed202..e82e327e 100644 --- a/src/components/Posts/Posts.tsx +++ b/src/components/Posts/Posts.tsx @@ -1,6 +1,6 @@ import {Stack, Typography} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {FC} from 'react' import {useSeminarInfo} from '@/utils/useSeminarInfo' @@ -15,7 +15,7 @@ export const Posts: FC = () => { error: postsError, } = useQuery({ queryKey: ['cms', 'post', 'visible'], - queryFn: () => axios.get(`/api/cms/post/visible?sites=${seminarId}`), + queryFn: () => apiAxios.get(`/api/cms/post/visible?sites=${seminarId}`), }) const posts = postsData?.data ?? [] diff --git a/src/components/ProblemAdministration/ProblemAdministration.tsx b/src/components/ProblemAdministration/ProblemAdministration.tsx index eec9a28e..13751c6f 100644 --- a/src/components/ProblemAdministration/ProblemAdministration.tsx +++ b/src/components/ProblemAdministration/ProblemAdministration.tsx @@ -1,7 +1,7 @@ import {FormatAlignJustify, Grading} from '@mui/icons-material' import {Stack, Typography} from '@mui/material' import {useMutation, useQuery} from '@tanstack/react-query' -import axios, {isAxiosError} from 'axios' +import {isAxiosError} from 'axios' import {useRouter} from 'next/router' import React, {FC, useCallback, useEffect, useState} from 'react' import {DropzoneOptions, useDropzone} from 'react-dropzone' @@ -20,6 +20,7 @@ import {FileUploader} from '../FileUploader/FileUploader' import {Loading} from '../Loading/Loading' import {Markdown} from '../Markdown/Markdown' import styles from './ProblemAdministration.module.scss' +import {apiAxios} from '@/api/apiAxios' export const ProblemAdministration: FC = () => { const router = useRouter() @@ -44,7 +45,7 @@ export const ProblemAdministration: FC = () => { isLoading: problemIsLoading, } = useQuery({ queryKey: ['competition', 'problem-administration', problemId], - queryFn: () => axios.get(`/api/competition/problem-administration/${problemId}`), + queryFn: () => apiAxios.get(`/api/competition/problem-administration/${problemId}`), // router.query.params su v prvom renderi undefined, tak pustime query az so spravnym problemId enabled: problemId !== undefined, }) @@ -53,7 +54,7 @@ export const ProblemAdministration: FC = () => { const semesterId = problem?.series.semester const {data: semesterData, isLoading: semesterIsLoading} = useQuery({ queryKey: ['competition', 'semester', semesterId], - queryFn: () => axios.get(`/api/competition/semester/${semesterId}`), + queryFn: () => apiAxios.get(`/api/competition/semester/${semesterId}`), // router.query.params su v prvom renderi undefined, tak pustime query az so spravnym semesterId enabled: semesterId !== undefined, }) @@ -76,11 +77,10 @@ export const ProblemAdministration: FC = () => { }, [problem]) const {mutate: uploadPoints} = useMutation({ - mutationFn: (id: string) => { - return axios.post(`/api/competition/problem-administration/${id}/upload-points`, { + mutationFn: (id: string) => + apiAxios.post(`/api/competition/problem-administration/${id}/upload-points`, { solution_set: solutions, - }) - }, + }), onSuccess: () => refetchProblem(), }) @@ -114,7 +114,7 @@ export const ProblemAdministration: FC = () => { const {mutate: uploadZipFile, error: uploadZipFileError} = useMutation({ mutationFn: ({data, problemId}: {data: FormData; problemId?: string}) => - axios.post(`/api/competition/problem/${problemId}/upload-corrected`, data), + apiAxios.post(`/api/competition/problem/${problemId}/upload-corrected`, data), onSuccess: () => refetchProblem(), }) diff --git a/src/components/Problems/Discussion.tsx b/src/components/Problems/Discussion.tsx index ec4b91c6..3b7a45e0 100644 --- a/src/components/Problems/Discussion.tsx +++ b/src/components/Problems/Discussion.tsx @@ -1,8 +1,8 @@ import {Stack, Typography} from '@mui/material' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' -import axios from 'axios' import {FC, useState} from 'react' +import {apiAxios} from '@/api/apiAxios' import {Comment, CommentState} from '@/types/api/competition' import {Profile} from '@/types/api/personal' import {AuthContainer} from '@/utils/AuthContainer' @@ -28,7 +28,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer const queryKey = ['competition', 'problem', problemId, 'comments'] const {data: commentsData, isLoading: commentsIsLoading} = useQuery({ queryKey, - queryFn: () => axios.get(`/api/competition/problem/${problemId}/comments`), + queryFn: () => apiAxios.get(`/api/competition/problem/${problemId}/comments`), }) const comments = commentsData?.data @@ -38,7 +38,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer const {data} = useQuery({ queryKey: ['personal', 'profiles', 'myprofile'], - queryFn: () => axios.get(`/api/personal/profiles/myprofile`), + queryFn: () => apiAxios.get(`/api/personal/profiles/myprofile`), enabled: isAuthed, }) const userId = data?.data.id @@ -54,7 +54,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer } const {mutate: addComment} = useMutation({ - mutationFn: () => axios.post(`/api/competition/problem/${problemId}/add-comment`, {text: commentText}), + mutationFn: () => apiAxios.post(`/api/competition/problem/${problemId}/add-comment`, {text: commentText}), onSuccess: () => { setCommentText('') invalidateCommentsAndCount() @@ -62,7 +62,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer }) const {mutate: publishComment} = useMutation({ - mutationFn: (id: number) => axios.post(`/api/competition/comment/${id}/publish`), + mutationFn: (id: number) => apiAxios.post(`/api/competition/comment/${id}/publish`), onSuccess: () => { invalidateCommentsAndCount() }, @@ -70,7 +70,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer const {mutate: hideComment} = useMutation({ mutationFn: ({id, hiddenResponseText}: {id: number; hiddenResponseText: string}) => - axios.post(`/api/competition/comment/${id}/hide`, {hidden_response: hiddenResponseText}), + apiAxios.post(`/api/competition/comment/${id}/hide`, {hidden_response: hiddenResponseText}), onSuccess: () => { invalidateCommentsAndCount() sethiddenResponseDialogId(-1) @@ -79,7 +79,7 @@ export const Discussion: FC = ({problemId, invalidateSeriesQuer }) const {mutate: confirmDeleteComment} = useMutation({ - mutationFn: (id: number) => axios.delete(`/api/competition/comment/${id}`), + mutationFn: (id: number) => apiAxios.delete(`/api/competition/comment/${id}`), onSuccess: () => { invalidateCommentsAndCount() }, diff --git a/src/components/Problems/Problems.tsx b/src/components/Problems/Problems.tsx index 236ff9f3..50cd59d9 100644 --- a/src/components/Problems/Problems.tsx +++ b/src/components/Problems/Problems.tsx @@ -1,6 +1,6 @@ import {Stack, Typography} from '@mui/material' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useRouter} from 'next/router' import {FC, useEffect, useState} from 'react' import {useInterval} from 'usehooks-ts' @@ -30,7 +30,7 @@ export const Problems: FC = () => { const {data} = useQuery({ queryKey: ['personal', 'profiles', 'myprofile'], - queryFn: () => axios.get(`/api/personal/profiles/myprofile`), + queryFn: () => apiAxios.get(`/api/personal/profiles/myprofile`), enabled: isAuthed, }) const profile = data?.data @@ -47,13 +47,13 @@ export const Problems: FC = () => { const {data: seriesData, isLoading: seriesIsLoading} = useQuery({ queryKey: ['competition', 'series', id.seriesId], - queryFn: () => axios.get(`/api/competition/series/${id.seriesId}`), + queryFn: () => apiAxios.get(`/api/competition/series/${id.seriesId}`), enabled: id.seriesId !== -1, }) const {data: bannerMessage, isLoading: isBannerLoading} = useQuery({ queryKey: ['cms', 'info-banner', 'series-problems', id.seriesId], - queryFn: () => axios.get(`/api/cms/info-banner/series-problems/${id.seriesId}`), + queryFn: () => apiAxios.get(`/api/cms/info-banner/series-problems/${id.seriesId}`), enabled: id.seriesId !== -1, }) @@ -87,7 +87,7 @@ export const Problems: FC = () => { }, [setBannerMessages, isBannerLoading, bannerMessages]) const {mutate: registerToSemester} = useMutation({ - mutationFn: (id: number) => axios.post(`/api/competition/event/${id}/register`), + mutationFn: (id: number) => apiAxios.post(`/api/competition/event/${id}/register`), onSuccess: () => { // refetch semestra, nech sa aktualizuje is_registered invalidateSeriesQuery() diff --git a/src/components/Problems/UploadProblemForm.tsx b/src/components/Problems/UploadProblemForm.tsx index ae904202..15688d7d 100644 --- a/src/components/Problems/UploadProblemForm.tsx +++ b/src/components/Problems/UploadProblemForm.tsx @@ -1,9 +1,9 @@ import {Typography} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios from 'axios' import {Dispatch, FC, SetStateAction, useState} from 'react' import {useDropzone} from 'react-dropzone' +import {apiAxios} from '@/api/apiAxios' import {CloseButton} from '@/components/CloseButton/CloseButton' import {Accept} from '@/utils/dropzone-accept' import {niceBytes} from '@/utils/niceBytes' @@ -33,7 +33,8 @@ export const UploadProblemForm: FC<{ const {alert} = useAlert() const {mutate: uploadSolution} = useMutation({ - mutationFn: (formData: FormData) => axios.post(`/api/competition/problem/${problemId}/upload-solution`, formData), + mutationFn: (formData: FormData) => + apiAxios.post(`/api/competition/problem/${problemId}/upload-solution`, formData), onSuccess: (response) => { if (response.status === 201) { // refetch serie, nech sa aktualizuje problem.submitted diff --git a/src/components/Profile/PasswordChangeForm.tsx b/src/components/Profile/PasswordChangeForm.tsx index ba1d15e3..629957db 100644 --- a/src/components/Profile/PasswordChangeForm.tsx +++ b/src/components/Profile/PasswordChangeForm.tsx @@ -1,7 +1,7 @@ import {Visibility, VisibilityOff} from '@mui/icons-material' import {IconButton, Stack} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios, {AxiosError} from 'axios' +import {AxiosError} from 'axios' import {FC, useState} from 'react' import {SubmitHandler, useForm} from 'react-hook-form' @@ -11,6 +11,7 @@ import {useAlert} from '@/utils/useAlert' import {Button} from '../Clickable/Button' import {Dialog} from '../Dialog/Dialog' import {FormInput} from '../FormItems/FormInput/FormInput' +import {apiAxios} from '@/api/apiAxios' type PasswordChangeDialogValues = { old_password: string @@ -53,7 +54,7 @@ export const PasswordChangeDialog: FC = ({open, close const {mutate: submitFormData} = useMutation({ mutationFn: (data: PasswordChangeDialogValues) => { - return axios.post(`/api/user/password/change`, data) + return apiAxios.post(`/api/user/password/change`, data) }, onSuccess: onSuccess, onError: onError, diff --git a/src/components/Profile/ProfileDetail.tsx b/src/components/Profile/ProfileDetail.tsx index 6103f07f..a1af1241 100644 --- a/src/components/Profile/ProfileDetail.tsx +++ b/src/components/Profile/ProfileDetail.tsx @@ -1,6 +1,6 @@ import {Stack, Typography} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {FC, useState} from 'react' import {Button} from '@/components/Clickable/Button' @@ -41,7 +41,7 @@ export const ProfileDetail: FC = () => { const {data} = useQuery({ queryKey: ['personal', 'profiles', 'myprofile'], - queryFn: () => axios.get(`/api/personal/profiles/myprofile`), + queryFn: () => apiAxios.get(`/api/personal/profiles/myprofile`), enabled: isAuthed, }) const profile = data?.data diff --git a/src/components/Profile/ProfileForm.tsx b/src/components/Profile/ProfileForm.tsx index 23c297a6..fec25160 100644 --- a/src/components/Profile/ProfileForm.tsx +++ b/src/components/Profile/ProfileForm.tsx @@ -1,10 +1,10 @@ import {Stack} from '@mui/material' import {useMutation, useQuery} from '@tanstack/react-query' -import axios from 'axios' import {useRouter} from 'next/router' import {FC} from 'react' import {SubmitHandler, useForm} from 'react-hook-form' +import {apiAxios} from '@/api/apiAxios' import {FormInput} from '@/components/FormItems/FormInput/FormInput' import {SelectOption} from '@/components/FormItems/FormSelect/FormSelect' import {IGeneralPostResponse} from '@/types/api/general' @@ -39,7 +39,7 @@ export const ProfileForm: FC = () => { const {data} = useQuery({ queryKey: ['personal', 'profiles', 'myprofile'], - queryFn: () => axios.get(`/api/personal/profiles/myprofile`), + queryFn: () => apiAxios.get(`/api/personal/profiles/myprofile`), enabled: isAuthed, }) const profile = data?.data @@ -87,7 +87,7 @@ export const ProfileForm: FC = () => { const {mutate: submitFormData} = useMutation({ mutationFn: (data: ProfileFormValues) => { - return axios.put(`/api/user/user`, transformFormData(data)) + return apiAxios.put(`/api/user/user`, transformFormData(data)) }, onSuccess: () => router.push(`/${seminar}/profil`), }) diff --git a/src/components/RegisterForm/RegisterForm.tsx b/src/components/RegisterForm/RegisterForm.tsx index 6b87df9c..54311530 100644 --- a/src/components/RegisterForm/RegisterForm.tsx +++ b/src/components/RegisterForm/RegisterForm.tsx @@ -1,7 +1,7 @@ import {Visibility, VisibilityOff} from '@mui/icons-material' import {IconButton, Stack, Typography} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios, {AxiosError} from 'axios' +import {AxiosError} from 'axios' import {useRouter} from 'next/router' import {FC, useState} from 'react' import {SubmitHandler, useForm, useFormState} from 'react-hook-form' @@ -16,6 +16,7 @@ import {Button} from '../Clickable/Button' import {Link} from '../Clickable/Link' import {Dialog} from '../Dialog/Dialog' import {SchoolSubForm, SchoolSubFormValues} from '../SchoolSubForm/SchoolSubForm' +import {apiAxios} from '@/api/apiAxios' interface RegisterFormValues extends SchoolSubFormValues { email?: string @@ -83,7 +84,7 @@ export const RegisterForm: FC = () => { const {mutate: submitFormData} = useMutation({ mutationFn: (data: RegisterFormValues) => { - return axios.post(`/api/user/registration?seminar=${seminar}`, transformFormData(data)) + return apiAxios.post(`/api/user/registration?seminar=${seminar}`, transformFormData(data)) }, // TODO: show alert/toast and redirect to homepage instead of redirect to info page onSuccess: () => diff --git a/src/components/Results/Results.tsx b/src/components/Results/Results.tsx index 36f93680..fa9546a8 100644 --- a/src/components/Results/Results.tsx +++ b/src/components/Results/Results.tsx @@ -1,6 +1,6 @@ import {Box} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {FC, useEffect} from 'react' import {BannerContainer} from '@/utils/BannerContainer' @@ -17,14 +17,14 @@ export const Results: FC = () => { const {data: resultsData, isLoading: resultsIsLoading} = useQuery({ queryKey: ['competition', competitionEndpoint, idForEndpoint, 'results'], - queryFn: () => axios.get(`/api/competition/${competitionEndpoint}/${idForEndpoint}/results`), + queryFn: () => apiAxios.get(`/api/competition/${competitionEndpoint}/${idForEndpoint}/results`), enabled: id.semesterId !== -1 || id.seriesId !== -1, }) const results = resultsData?.data ?? [] const {setBannerMessages} = BannerContainer.useContainer() const {data: bannerMessage, isLoading: isBannerLoading} = useQuery({ queryKey: ['cms', 'info-banner', 'series-results', id.seriesId], - queryFn: () => axios.get(`/api/cms/info-banner/series-results/${id.seriesId}`), + queryFn: () => apiAxios.get(`/api/cms/info-banner/series-results/${id.seriesId}`), enabled: id.seriesId !== -1, }) diff --git a/src/components/SchoolSubForm/SchoolSubForm.tsx b/src/components/SchoolSubForm/SchoolSubForm.tsx index a6f29004..6a70c165 100644 --- a/src/components/SchoolSubForm/SchoolSubForm.tsx +++ b/src/components/SchoolSubForm/SchoolSubForm.tsx @@ -1,6 +1,6 @@ import {Stack} from '@mui/material' import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useEffect, useRef} from 'react' import {Control, UseFormSetValue, UseFormWatch} from 'react-hook-form' @@ -39,7 +39,7 @@ export const SchoolSubForm = ({control, watch, setValue, gap}: SchoolSubFormProp // načítanie ročníkov z BE, ktorými vyplníme FormSelect s ročníkmi const {data: gradesData} = useQuery({ queryKey: ['competition', 'grade'], - queryFn: () => axios.get(`/api/competition/grade`), + queryFn: () => apiAxios.get(`/api/competition/grade`), }) const grades = gradesData?.data ?? [] const gradeItems: SelectOption[] = grades.map(({id, name}) => ({id, label: name})) @@ -47,7 +47,7 @@ export const SchoolSubForm = ({control, watch, setValue, gap}: SchoolSubFormProp // načítanie škôl z BE, ktorými vyplníme FormAutocomplete so školami const {data: schoolsData} = useQuery({ queryKey: ['personal', 'schools'], - queryFn: () => axios.get(`/api/personal/schools`), + queryFn: () => apiAxios.get(`/api/personal/schools`), }) const schools = (schoolsData?.data ?? []).sort((a, b) => a.name.localeCompare(b.name)) const allSchoolItems: SelectOption[] = schools.map(({code, city, name, street}) => ({ diff --git a/src/components/SemesterAdministration/SemesterAdministration.tsx b/src/components/SemesterAdministration/SemesterAdministration.tsx index 61d97e2c..47311c0e 100644 --- a/src/components/SemesterAdministration/SemesterAdministration.tsx +++ b/src/components/SemesterAdministration/SemesterAdministration.tsx @@ -1,7 +1,7 @@ import {Stack, Typography} from '@mui/material' import Grid from '@mui/material/Unstable_Grid2' import {useMutation, useQuery} from '@tanstack/react-query' -import axios, {AxiosError} from 'axios' +import {AxiosError} from 'axios' import {FC, Fragment, useState} from 'react' import {Button} from '@/components/Clickable/Button' @@ -14,6 +14,7 @@ import {useHasPermissions} from '@/utils/useHasPermissions' import {Loading} from '../Loading/Loading' import {PublicationUploader} from '../PublicationUploader/PublicationUploader' import {Result} from '../Results/ResultsRow' +import {apiAxios} from '@/api/apiAxios' interface PostalCard { code: number @@ -40,7 +41,7 @@ export const SemesterAdministration: FC = () => { refetch, } = useQuery({ queryKey: ['competition', 'semester', semesterId], - queryFn: () => axios.get(`/api/competition/semester/${semesterId}`), + queryFn: () => apiAxios.get(`/api/competition/semester/${semesterId}`), // router.query.params su v prvom renderi undefined, tak pustime query az so spravnym semesterId enabled: semesterId !== undefined, }) @@ -50,7 +51,7 @@ export const SemesterAdministration: FC = () => { const getResults = async (seriesId: number | null) => { const isSemester = seriesId === null - const {data} = await axios.get( + const {data} = await apiAxios.get( isSemester ? `/api/competition/semester/${semesterId}/results` : `/api/competition/series/${seriesId}/results`, ) setTextareaContent( @@ -79,7 +80,7 @@ export const SemesterAdministration: FC = () => { } const getPostalCards = async (offline_only: boolean) => { - const {data} = await axios.get( + const {data} = await apiAxios.get( `/api/competition/semester/${semesterId}/${offline_only ? 'offline-schools' : 'schools'}`, ) setTextareaContent( @@ -92,7 +93,7 @@ export const SemesterAdministration: FC = () => { const [seriesFreezeErrors, setSeriesFreezeErrors] = useState>() const {mutate: freezeSeries} = useMutation({ - mutationFn: (series: SeriesWithProblems) => axios.post(`/api/competition/series/${series.id}/results/freeze`), + mutationFn: (series: SeriesWithProblems) => apiAxios.post(`/api/competition/series/${series.id}/results/freeze`), onSuccess: (_, variables: SeriesWithProblems) => { setSeriesFreezeErrors((prev) => new Map(prev).set(variables.id, '')) refetch() diff --git a/src/components/VerifyEmail/VerifyEmail.tsx b/src/components/VerifyEmail/VerifyEmail.tsx index 5fc38fa1..4bedb590 100644 --- a/src/components/VerifyEmail/VerifyEmail.tsx +++ b/src/components/VerifyEmail/VerifyEmail.tsx @@ -1,6 +1,6 @@ import {Stack, Typography} from '@mui/material' import {useMutation} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useRouter} from 'next/router' import {FC, useEffect, useState} from 'react' @@ -23,7 +23,8 @@ export const VerifyEmail: FC = () => { isError, isSuccess: isVerified, } = useMutation({ - mutationFn: (verificationKey: string) => axios.post('/api/user/registration/verify-email', {key: verificationKey}), + mutationFn: (verificationKey: string) => + apiAxios.post('/api/user/registration/verify-email', {key: verificationKey}), }) useEffect(() => { diff --git a/src/pages/strom/[page].tsx b/src/pages/strom/[page].tsx index c9006b8b..12c8fe7c 100644 --- a/src/pages/strom/[page].tsx +++ b/src/pages/strom/[page].tsx @@ -1,4 +1,4 @@ -import axios from 'axios' +import {serverApiAxios} from '@/api/apiAxios' import {GetServerSideProps, NextPage} from 'next' import {Markdown} from '@/components/Markdown/Markdown' @@ -27,9 +27,7 @@ export const seminarBasedGetServerSideProps = // tento check je hlavne pre typescript - parameter `page` by vzdy mal existovat a vzdy ako string if (query?.page && typeof query.page === 'string') { const requestedUrl = query.page - const {data} = await axios.get( - `${process.env.NEXT_PUBLIC_BE_PROTOCOL}://${process.env.NEXT_PUBLIC_BE_HOSTNAME}:${process.env.NEXT_PUBLIC_BE_PORT}/cms/flat-page/by-url/${requestedUrl}`, - ) + const {data} = await serverApiAxios.get(`/cms/flat-page/by-url/${requestedUrl}`) // ked stranka neexistuje, vrati sa `content: ""`. teraz renderujeme stranku len ked je content neprazdny a server rovno vrati redirect. // druha moznost by bola nechat prazdny content handlovat clienta - napriklad zobrazit custom error, ale nechat usera na neplatnej stranke. // tretia moznost je miesto redirectu vratit nextovsku 404 diff --git a/src/pages/strom/akcie/[[...params]].tsx b/src/pages/strom/akcie/[[...params]].tsx index 7cf9ebbf..3ed10e39 100644 --- a/src/pages/strom/akcie/[[...params]].tsx +++ b/src/pages/strom/akcie/[[...params]].tsx @@ -1,4 +1,4 @@ -import axios from 'axios' +import {serverApiAxios} from '@/api/apiAxios' import {GetServerSideProps, NextPage} from 'next' import {CompetitionPage} from '@/components/CompetitionPage/CompetitionPage' @@ -44,8 +44,8 @@ export const competitionBasedGetServerSideProps = const requestedUrl = query.params[0] try { - const {data} = await axios.get( - `${process.env.NEXT_PUBLIC_BE_PROTOCOL}://${process.env.NEXT_PUBLIC_BE_HOSTNAME}:${process.env.NEXT_PUBLIC_BE_PORT}/competition/competition/slug/${requestedUrl}`, + const {data} = await serverApiAxios.get( + `/competition/competition/slug/${requestedUrl}`, ) if (!data) return redirectToSeminar diff --git a/src/utils/AuthContainer.tsx b/src/utils/AuthContainer.tsx index fca075f3..032c7f3f 100644 --- a/src/utils/AuthContainer.tsx +++ b/src/utils/AuthContainer.tsx @@ -1,29 +1,27 @@ import {useMutation, useQueryClient} from '@tanstack/react-query' -import axios, {AxiosError} from 'axios' +import {AxiosError} from 'axios' import {useEffect, useState} from 'react' -import {Cookies} from 'react-cookie' import {createContainer} from 'unstated-next' +import {apiAxios, newApiAxios} from '@/api/apiAxios' import {Login, Token} from '@/types/api/generated/user' import {MyPermissions} from '@/types/api/personal' -// special axios instance to prevent interceptors -const specialAxios = axios.create() +// specialna axios instancia bez error handlingu pridaneho do `apiAxios` nizsie +const specialApiAxios = newApiAxios('client') -export const testAuthRequest = async () => specialAxios.get('/api/personal/profiles/mypermissions') +export const testAuthRequest = async () => specialApiAxios.get('/api/personal/profiles/mypermissions') // call na lubovolny "auth" endpoint ako test prihlasenia, vracia true/false podla uspesnosti export const testAuth = async () => { try { await testAuthRequest() return true - } catch (e: unknown) { + } catch { return false } } -const cookies = new Cookies() - const useAuth = () => { // stav, ktory napoveda, ci mame sessionid cookie a vieme robit auth requesty const [isAuthed, setIsAuthed] = useState(false) @@ -34,18 +32,6 @@ const useAuth = () => { const success = await testAuth() if (success) setIsAuthed(true) })() - - // interceptor pre auth - axios.interceptors.request.use((config) => { - config.headers = config.headers ?? {} - // auth pozostava z comba: - // 1. `sessionid` httpOnly cookie ktoru nastavuje aj maze server pri login/logout - // 2. tato CSRF hlavicka, ktora ma obsahovat cookie, ktoru nastavuje server - config.headers['X-CSRFToken'] = cookies.get('csrftoken') - - return config - }) - // one-time vec pri prvom nacitani stranky }, []) @@ -56,7 +42,7 @@ const useAuth = () => { useEffect(() => { if (isAuthed) { // ked sme authed a dostaneme 403, chceme overit, ci nam vyprsalo prihlasenie - ak hej, chceme odhlasit usera, nech vie, co sa deje - const responseInterceptor = axios.interceptors.response.use( + const responseInterceptor = apiAxios.interceptors.response.use( (response) => response, async (error: AxiosError) => { const status = error.response?.status @@ -81,13 +67,13 @@ const useAuth = () => { // useEffect unmount callback return () => { - axios.interceptors.response.eject(responseInterceptor) + apiAxios.interceptors.response.eject(responseInterceptor) } } }, [isAuthed]) const {mutate: login, mutateAsync: loginAsync} = useMutation({ - mutationFn: ({data}: {data: Login; onSuccess?: () => void}) => axios.post('/api/user/login', data), + mutationFn: ({data}: {data: Login; onSuccess?: () => void}) => apiAxios.post('/api/user/login', data), onSuccess: async (_, {onSuccess}) => { onSuccess?.() @@ -99,7 +85,7 @@ const useAuth = () => { // zavoláme logout API point, ktorý zmaže token na BE a odstráni sessionid cookie. const {mutate: logout, mutateAsync: logoutAsync} = useMutation({ - mutationFn: () => axios.post('/api/user/logout'), + mutationFn: () => apiAxios.post('/api/user/logout'), onSettled: () => { setIsAuthed(false) // sessionid cookie odstrani server sam diff --git a/src/utils/urlBase.ts b/src/utils/urlBase.ts new file mode 100644 index 00000000..037c684d --- /dev/null +++ b/src/utils/urlBase.ts @@ -0,0 +1,12 @@ +export const composeUrlBase = (protocol: string, hostname: string, port?: string) => + `${protocol}://${hostname}${port ? `:${port}` : ''}` + +export const getPublicServerUrl = () => + composeUrlBase( + process.env.NEXT_PUBLIC_BE_PROTOCOL as string, + process.env.NEXT_PUBLIC_BE_HOSTNAME as string, + process.env.NEXT_PUBLIC_BE_PORT, + ) + +export const getInternalServerUrl = () => + composeUrlBase(process.env.BE_PROTOCOL as string, process.env.BE_HOSTNAME as string, process.env.BE_PORT) diff --git a/src/utils/useDataFromURL.tsx b/src/utils/useDataFromURL.tsx index a58d9ffe..3073c04b 100644 --- a/src/utils/useDataFromURL.tsx +++ b/src/utils/useDataFromURL.tsx @@ -1,5 +1,5 @@ import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {useRouter} from 'next/router' import {useMemo} from 'react' @@ -12,7 +12,7 @@ export const useDataFromURL = () => { const {data: semesterListData, isLoading: semesterListIsLoading} = useQuery({ queryKey: ['competition', 'semester-list', {competition: seminarId}], - queryFn: () => axios.get(`/api/competition/semester-list?competition=${seminarId}`), + queryFn: () => apiAxios.get(`/api/competition/semester-list?competition=${seminarId}`), }) // memoized because the array fallback would create new object on each render, which would ruin seriesId memoization as semesterList is a dependency const semesterList = useMemo(() => semesterListData?.data || [], [semesterListData]) @@ -21,7 +21,7 @@ export const useDataFromURL = () => { // - napr. prideme na `/zadania` cez menu, nie na `/zadania/44/leto/2` const {data: currentSeriesData, isLoading: currentSeriesIsLoading} = useQuery({ queryKey: ['competition', 'series', 'current', seminarId], - queryFn: () => axios.get(`/api/competition/series/current/` + seminarId), + queryFn: () => apiAxios.get(`/api/competition/series/current/` + seminarId), }) const currentSeriesId = currentSeriesData?.data.id ?? -1 const currentSemesterId = currentSeriesData?.data.semester ?? -1 diff --git a/src/utils/useHasPermissions.ts b/src/utils/useHasPermissions.ts index 5072aab1..c4e2617b 100644 --- a/src/utils/useHasPermissions.ts +++ b/src/utils/useHasPermissions.ts @@ -1,5 +1,5 @@ import {useQuery} from '@tanstack/react-query' -import axios from 'axios' +import {apiAxios} from '@/api/apiAxios' import {MyPermissions} from '@/types/api/personal' @@ -11,7 +11,7 @@ export const useHasPermissions = () => { const {data, isLoading: permissionsIsLoading} = useQuery({ queryKey: ['personal', 'profiles', 'mypermissions'], - queryFn: () => axios.get('/api/personal/profiles/mypermissions'), + queryFn: () => apiAxios.get('/api/personal/profiles/mypermissions'), enabled: isAuthed, }) diff --git a/yarn.lock b/yarn.lock index c4d7cf6f..9f837a7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3382,14 +3382,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.7.7": - version: 1.7.7 - resolution: "axios@npm:1.7.7" +"axios@npm:^1.7.9": + version: 1.7.9 + resolution: "axios@npm:1.7.9" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10/7f875ea13b9298cd7b40fd09985209f7a38d38321f1118c701520939de2f113c4ba137832fe8e3f811f99a38e12c8225481011023209a77b0c0641270e20cde1 + checksum: 10/b7a5f660ea53ba9c2a745bf5ad77ad8bf4f1338e13ccc3f9f09f810267d6c638c03dac88b55dae8dc98b79c57d2d6835be651d58d2af97c174f43d289a9fd007 languageName: node linkType: hard @@ -10225,7 +10225,7 @@ __metadata: "@types/react-router-dom": "npm:^5.3.3" "@typescript-eslint/eslint-plugin": "npm:^8.15.0" "@typescript-eslint/parser": "npm:^8.15.0" - axios: "npm:^1.7.7" + axios: "npm:^1.7.9" clsx: "npm:^2.1.1" confusing-browser-globals: "npm:^1.0.11" eslint: "npm:^8.57.1"