mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-17 12:33:22 +00:00
2 - jwt final
This commit is contained in:
@@ -24,8 +24,8 @@ type Props = {
|
|||||||
export const SignIn = (props: Props) => {
|
export const SignIn = (props: Props) => {
|
||||||
const { control, handleSubmit } = useForm<FormType>({
|
const { control, handleSubmit } = useForm<FormType>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
email: '',
|
email: 'test@test.com',
|
||||||
password: '',
|
password: 'test',
|
||||||
rememberMe: false,
|
rememberMe: false,
|
||||||
},
|
},
|
||||||
mode: 'onSubmit',
|
mode: 'onSubmit',
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const privateRoutes: RouteObject[] = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
export const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,33 @@
|
|||||||
import { flashcardsApi } from '..'
|
import { flashcardsApi } from '..'
|
||||||
import { LoginArgs, User } from './auth.types'
|
import { LoginArgs, LoginResponse, User } from './auth.types'
|
||||||
|
|
||||||
export const authService = flashcardsApi.injectEndpoints({
|
export const authService = flashcardsApi.injectEndpoints({
|
||||||
endpoints: builder => ({
|
endpoints: builder => ({
|
||||||
login: builder.mutation<void, LoginArgs>({
|
login: builder.mutation<LoginResponse, LoginArgs>({
|
||||||
invalidatesTags: ['Me'],
|
async onQueryStarted(
|
||||||
query: body => ({
|
// 1 параметр: QueryArg - аргументы, которые приходят в query
|
||||||
body,
|
_,
|
||||||
method: 'POST',
|
// 2 параметр: MutationLifecycleApi - dispatch, queryFulfilled, getState и пр.
|
||||||
url: '/v1/auth/login',
|
// 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>({
|
me: builder.query<User, void>({
|
||||||
providesTags: ['Me'],
|
providesTags: ['Me'],
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ export type LoginArgs = {
|
|||||||
password: string
|
password: string
|
||||||
rememberMe?: boolean
|
rememberMe?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LoginResponse = {
|
||||||
|
accessToken: string
|
||||||
|
refreshToken: string
|
||||||
|
}
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
avatar: null | string
|
avatar: null | string
|
||||||
created: 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({
|
export const flashcardsApi = createApi({
|
||||||
baseQuery: fetchBaseQuery({
|
baseQuery: baseQueryWithReauth,
|
||||||
baseUrl: 'https://api.flashcards.andrii.es',
|
|
||||||
credentials: 'include',
|
|
||||||
prepareHeaders: headers => {
|
|
||||||
headers.append('x-auth-skip', 'true')
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
endpoints: () => ({}),
|
endpoints: () => ({}),
|
||||||
reducerPath: 'flashcardsApi',
|
reducerPath: 'flashcardsApi',
|
||||||
tagTypes: ['Decks', 'Me'],
|
tagTypes: ['Decks', 'Me'],
|
||||||
|
|||||||
Reference in New Issue
Block a user