mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-16 20:59:27 +00:00
2 - jwt final
This commit is contained in:
@@ -24,8 +24,8 @@ type Props = {
|
||||
export const SignIn = (props: Props) => {
|
||||
const { control, handleSubmit } = useForm<FormType>({
|
||||
defaultValues: {
|
||||
email: '',
|
||||
password: '',
|
||||
email: 'test@test.com',
|
||||
password: 'test',
|
||||
rememberMe: false,
|
||||
},
|
||||
mode: 'onSubmit',
|
||||
|
||||
@@ -34,7 +34,7 @@ const privateRoutes: RouteObject[] = [
|
||||
},
|
||||
]
|
||||
|
||||
const router = createBrowserRouter([
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
import { flashcardsApi } from '..'
|
||||
import { LoginArgs, User } from './auth.types'
|
||||
import { LoginArgs, LoginResponse, User } from './auth.types'
|
||||
|
||||
export const authService = flashcardsApi.injectEndpoints({
|
||||
endpoints: builder => ({
|
||||
login: builder.mutation<void, LoginArgs>({
|
||||
invalidatesTags: ['Me'],
|
||||
query: body => ({
|
||||
body,
|
||||
method: 'POST',
|
||||
url: '/v1/auth/login',
|
||||
}),
|
||||
login: builder.mutation<LoginResponse, LoginArgs>({
|
||||
async onQueryStarted(
|
||||
// 1 параметр: QueryArg - аргументы, которые приходят в query
|
||||
_,
|
||||
// 2 параметр: MutationLifecycleApi - dispatch, queryFulfilled, getState и пр.
|
||||
// queryFulfilled - это промис, возвращаемый RTK Query, который разрешается,
|
||||
// когда запрос успешно завершен
|
||||
{ queryFulfilled }
|
||||
) {
|
||||
const { data } = await queryFulfilled
|
||||
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
|
||||
localStorage.setItem('accessToken', data.accessToken)
|
||||
localStorage.setItem('refreshToken', data.refreshToken)
|
||||
},
|
||||
query: body => {
|
||||
return {
|
||||
body,
|
||||
method: 'POST',
|
||||
url: '/v1/auth/login',
|
||||
}
|
||||
},
|
||||
}),
|
||||
me: builder.query<User, void>({
|
||||
providesTags: ['Me'],
|
||||
|
||||
@@ -3,6 +3,12 @@ export type LoginArgs = {
|
||||
password: string
|
||||
rememberMe?: boolean
|
||||
}
|
||||
|
||||
export type LoginResponse = {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
|
||||
export type User = {
|
||||
avatar: null | string
|
||||
created: string
|
||||
|
||||
81
src/services/flashCardsBaseQuery.ts
Normal file
81
src/services/flashCardsBaseQuery.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { router } from '@/router'
|
||||
import {
|
||||
BaseQueryFn,
|
||||
FetchArgs,
|
||||
FetchBaseQueryError,
|
||||
fetchBaseQuery,
|
||||
} from '@reduxjs/toolkit/query/react'
|
||||
import { Mutex } from 'async-mutex'
|
||||
|
||||
// create a new mutex
|
||||
const mutex = new Mutex()
|
||||
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: 'https://api.flashcards.andrii.es',
|
||||
prepareHeaders: headers => {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
|
||||
if (headers.get('Authorization')) {
|
||||
return headers
|
||||
}
|
||||
|
||||
if (token) {
|
||||
headers.set('Authorization', `Bearer ${token}`)
|
||||
}
|
||||
|
||||
return headers
|
||||
},
|
||||
})
|
||||
|
||||
export const baseQueryWithReauth: BaseQueryFn<
|
||||
FetchArgs | string,
|
||||
unknown,
|
||||
FetchBaseQueryError
|
||||
> = async (args, api, extraOptions) => {
|
||||
// wait until the mutex is available without locking it
|
||||
await mutex.waitForUnlock()
|
||||
let result = await baseQuery(args, api, extraOptions)
|
||||
|
||||
if (result.error && result.error.status === 401) {
|
||||
// checking whether the mutex is locked
|
||||
if (!mutex.isLocked()) {
|
||||
const release = await mutex.acquire()
|
||||
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refreshToken')
|
||||
const refreshResult = (await baseQuery(
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${refreshToken}`,
|
||||
},
|
||||
method: 'POST',
|
||||
url: '/v2/auth/refresh-token',
|
||||
},
|
||||
api,
|
||||
extraOptions
|
||||
)) as any
|
||||
|
||||
console.log('refreshResult', refreshResult)
|
||||
|
||||
if (refreshResult.data) {
|
||||
localStorage.setItem('accessToken', refreshResult.data.accessToken)
|
||||
localStorage.setItem('refreshToken', refreshResult.data.refreshToken)
|
||||
|
||||
// retry the initial query
|
||||
result = await baseQuery(args, api, extraOptions)
|
||||
} else {
|
||||
router.navigate('/login')
|
||||
}
|
||||
} finally {
|
||||
// release must be called once the mutex should be released again.
|
||||
release()
|
||||
}
|
||||
} else {
|
||||
// wait until the mutex is available without locking it
|
||||
await mutex.waitForUnlock()
|
||||
result = await baseQuery(args, api, extraOptions)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
||||
import { baseQueryWithReauth } from '@/services/flashCardsBaseQuery'
|
||||
import { createApi } from '@reduxjs/toolkit/query/react'
|
||||
|
||||
export const flashcardsApi = createApi({
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: 'https://api.flashcards.andrii.es',
|
||||
credentials: 'include',
|
||||
prepareHeaders: headers => {
|
||||
headers.append('x-auth-skip', 'true')
|
||||
},
|
||||
}),
|
||||
baseQuery: baseQueryWithReauth,
|
||||
endpoints: () => ({}),
|
||||
reducerPath: 'flashcardsApi',
|
||||
tagTypes: ['Decks', 'Me'],
|
||||
|
||||
Reference in New Issue
Block a user