From 59f1822f6535f26116b7deac34a45d8cb1ca03da Mon Sep 17 00:00:00 2001 From: Arshdeep54 Date: Mon, 10 Feb 2025 23:36:25 +0530 Subject: [PATCH] add client side caching Signed-off-by: Arshdeep54 --- src/api/admin/challengesAPI.js | 100 ++++++++++++++++++++++----------- src/api/admin/configureAPI.js | 12 +++- src/utils/cacheUtils.js | 82 +++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 src/utils/cacheUtils.js diff --git a/src/api/admin/challengesAPI.js b/src/api/admin/challengesAPI.js index 5aaa526..076e244 100644 --- a/src/api/admin/challengesAPI.js +++ b/src/api/admin/challengesAPI.js @@ -1,50 +1,86 @@ import UserService from "./usersAPI"; import axiosInstance from "../axiosInstance.js"; +import { cacheRequest } from "@/utils/cacheUtils"; + export default { async fetchAllChallenges() { - return await axiosInstance.get(`/api/info/challenges`); + try { + const request = new Request(`${axiosInstance.defaults.baseURL}/api/info/challenges`); + const response = await cacheRequest(request); + const data = await response.json(); + return { data }; + } catch (error) { + console.error('Cache fetch failed, falling back to direct API call:', error); + // Fall back to axios which already handles auth + return await axiosInstance.get(`/api/info/challenges`); + } }, async fetchChallengeByName(name) { - return await axiosInstance({ - method: "get", - url: `/api/info/challenge/${name}` - // data: postData - }); + try { + return await axiosInstance({ + method: "get", + url: `/api/info/challenge/${name}` + // data: postData + }); + } catch (error) { + console.error('Failed to fetch challenge by name:', error); + throw error; + } }, async fetchAllTags() { - return await axiosInstance.get(`/api/info/tags`); + try { + return await axiosInstance.get(`/api/info/tags`); + } catch (error) { + console.error('Failed to fetch all tags:', error); + throw error; + } }, async manageChalAction(name, action) { - let postData = new FormData(); - postData.append("name", name); - postData.append("action", action); - return await axiosInstance({ - method: "post", - url: `/api/manage/challenge/`, - data: postData - }); + try { + let postData = new FormData(); + postData.append("name", name); + postData.append("action", action); + return await axiosInstance({ + method: "post", + url: `/api/manage/challenge/`, + data: postData + }); + } catch (error) { + console.error('Failed to manage challenge action:', error); + throw error; + } }, async manageMultipleChalAction(name, action) { - let postData = new FormData(); - postData.append("names", name); - postData.append("action", action); - return await axiosInstance({ - method: "post", - url: `/api/manage/challenge/multiple/`, - data: postData - }); + try { + let postData = new FormData(); + postData.append("names", name); + postData.append("action", action); + return await axiosInstance({ + method: "post", + url: `/api/manage/challenge/multiple/`, + data: postData + }); + } catch (error) { + console.error('Failed to manage multiple challenge action:', error); + throw error; + } }, async createChallenge(file) { - let bodyFormData = new FormData(); - bodyFormData.append("file", file); - const response = await axiosInstance({ - method: "post", - url: `/api/manage/challenge/upload`, - data: bodyFormData, - headers: { "Content-Type": "multipart/form-data" } - }); - return response.data; + try { + let bodyFormData = new FormData(); + bodyFormData.append("file", file); + const response = await axiosInstance({ + method: "post", + url: `/api/manage/challenge/upload`, + data: bodyFormData, + headers: { "Content-Type": "multipart/form-data" } + }); + return response.data; + } catch (error) { + console.error('Failed to create challenge:', error); + throw error; + } } }; diff --git a/src/api/admin/configureAPI.js b/src/api/admin/configureAPI.js index 1a3a12b..7de83cd 100644 --- a/src/api/admin/configureAPI.js +++ b/src/api/admin/configureAPI.js @@ -1,5 +1,5 @@ import axiosInstance from "../axiosInstance.js"; - +import { cacheRequest } from "@/utils/cacheUtils"; export default { async updateConfigs(configs) { let bodyFormData = new FormData(); @@ -18,7 +18,15 @@ export default { }); }, async getConfigs() { - return await axiosInstance.get(`/api/info/competition-info`); + try { + const request = new Request(`${axiosInstance.defaults.baseURL}/api/info/competition-info`); + const response = await cacheRequest(request); + const data = await response.json(); + return { data }; + } catch (error) { + console.error('Cache fetch failed, falling back to direct API call:', error); + return await axiosInstance.get(`/api/info/competition-info`); + } }, async getLogo(imgUrl, imgName) { diff --git a/src/utils/cacheUtils.js b/src/utils/cacheUtils.js new file mode 100644 index 0000000..ecb2043 --- /dev/null +++ b/src/utils/cacheUtils.js @@ -0,0 +1,82 @@ +const CACHE_NAME = 'api-cache'; +const CACHE_DURATION = 5 * 60; // 5 minutes in seconds + +const isCacheValid = (response) => { + if (!response) return false; + + const cachedAt = response.headers.get('x-cached-at'); + if (!cachedAt) return false; + + const ageInSeconds = (Date.now() - new Date(cachedAt).getTime()) / 1000; + return ageInSeconds < CACHE_DURATION; +}; + +const addCacheHeaders = (response) => { + const headers = new Headers(response.headers); + headers.append('x-cached-at', new Date().toISOString()); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers + }); +}; + +// Get auth token from LoginUser +const getAuthToken = () => { + try { + const userInfo = JSON.parse(localStorage.getItem('userInfo')); + if (userInfo && userInfo.token) { + return `Bearer ${userInfo.token}`; + } + return null; + } catch (error) { + console.error('Error getting auth token:', error); + return null; + } +}; + +export const cacheRequest = async (request) => { + try { + const cache = await caches.open(CACHE_NAME); + + const cachedResponse = await cache.match(request); + if (cachedResponse && isCacheValid(cachedResponse)) { + console.log('Returning cached response'); + return cachedResponse; + } + + // Add auth header to the request + const headers = new Headers(request.headers); + const authToken = getAuthToken(); + if (authToken) { + headers.set('Authorization', authToken); + } + + const authenticatedRequest = new Request(request, { + headers + }); + + const response = await fetch(authenticatedRequest); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const responseWithCacheHeaders = addCacheHeaders(response.clone()); + + await cache.put(request, responseWithCacheHeaders); + + return response; + } catch (error) { + console.error('Cache operation failed:', error); + throw error; // Let the API caller handle the error + } +}; + +export const clearCache = async () => { + try { + const cache = await caches.open(CACHE_NAME); + await cache.delete(); + } catch (error) { + console.error('Failed to clear cache:', error); + } +};