From 0cf783fec16ee7822f5baa8b2814e132e823e017 Mon Sep 17 00:00:00 2001 From: andres Date: Fri, 20 Sep 2024 18:16:16 +0200 Subject: [PATCH] lesson 4 --- package.json | 1 + pnpm-lock.yaml | 10 +++++ src/pages/auth/login.tsx | 4 +- src/services/instagram.api.ts | 12 +---- src/services/instagram.base-query.ts | 66 ++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/services/instagram.base-query.ts diff --git a/package.json b/package.json index ea17c00..a4e4b98 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@radix-ui/react-slot": "^1.1.0", "@reduxjs/toolkit": "^2.2.7", "@types/react-timeago": "^4.1.7", + "async-mutex": "^0.5.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.439.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33d1917..eec5166 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: '@types/react-timeago': specifier: ^4.1.7 version: 4.1.7 + async-mutex: + specifier: ^0.5.0 + version: 0.5.0 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -2199,6 +2202,9 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -7740,6 +7746,10 @@ snapshots: dependencies: tslib: 2.7.0 + async-mutex@0.5.0: + dependencies: + tslib: 2.7.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 diff --git a/src/pages/auth/login.tsx b/src/pages/auth/login.tsx index 024f354..96d9fd6 100644 --- a/src/pages/auth/login.tsx +++ b/src/pages/auth/login.tsx @@ -35,7 +35,9 @@ export default function Login() { .unwrap() .then((data) => { localStorage.setItem('access_token', data.accessToken) - router.push('/') + const payload = data.accessToken.split('.')[1] + const id = JSON.parse(atob(payload)).userId + router.push(`/profile/${id}`) }) }) diff --git a/src/services/instagram.api.ts b/src/services/instagram.api.ts index 2538850..f64933a 100644 --- a/src/services/instagram.api.ts +++ b/src/services/instagram.api.ts @@ -10,21 +10,13 @@ import { UserPosts, UserProfile, } from '@/services/instagram.types' +import { baseQueryWithReauth } from '@/services/instagram.base-query' // Define a service using a base URL and expected endpoints export const instagramApi = createApi({ tagTypes: ['Posts'], reducerPath: 'instagramApi', - baseQuery: fetchBaseQuery({ - baseUrl: 'https://inctagram.work/api/', - prepareHeaders: (headers) => { - const token = localStorage.getItem('access_token') - if (token) { - headers.set('Authorization', `Bearer ${token}`) - } - return headers - }, - }), + baseQuery: baseQueryWithReauth, endpoints: (builder) => ({ getAllPublicPosts: builder.query< GetAllPostsResponse, diff --git a/src/services/instagram.base-query.ts b/src/services/instagram.base-query.ts new file mode 100644 index 0000000..6e906c1 --- /dev/null +++ b/src/services/instagram.base-query.ts @@ -0,0 +1,66 @@ +import { fetchBaseQuery } from '@reduxjs/toolkit/query' +import type { + BaseQueryFn, + FetchArgs, + FetchBaseQueryError, +} from '@reduxjs/toolkit/query' + +import { Mutex } from 'async-mutex' +import Router from 'next/router' + +// create a new mutex +const mutex = new Mutex() + +const baseQuery = fetchBaseQuery({ + baseUrl: 'https://inctagram.work/api/', + credentials: 'include', + prepareHeaders: (headers) => { + const token = localStorage.getItem('access_token') + if (token) { + headers.set('Authorization', `Bearer ${token}`) + } + return headers + }, +}) + +export const baseQueryWithReauth: BaseQueryFn< + string | FetchArgs, + unknown, + FetchBaseQueryError +> = async (args, api, extraOptions) => { + console.log(args) + // wait until the mutex is available without locking it + await mutex.waitForUnlock() + + let result = await baseQuery(args, api, extraOptions) + console.log(result) + if (result.error && result.error.status === 401) { + // checking whether the mutex is locked + if (!mutex.isLocked()) { + const release = await mutex.acquire() + try { + const refreshResult = (await baseQuery( + { url: '/v1/auth/update-tokens', method: 'POST' }, + api, + extraOptions + )) as any + console.log(refreshResult) + if (refreshResult.data) { + localStorage.setItem('access_token', refreshResult.data.accessToken) + // retry the initial query + result = await baseQuery(args, api, extraOptions) + } else { + await Router.push('/auth/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 +}