From 5c5d38ef05fe71549418737028ec3918b0552139 Mon Sep 17 00:00:00 2001 From: Bruno Perel Date: Sun, 15 Oct 2023 19:28:08 +0200 Subject: [PATCH] Allow a custom storage for the api caches --- apps/web/index.ts | 8 ++- apps/web/src/App.vue | 13 +++- apps/web/src/api.ts | 68 +++++++++++++++++++ apps/web/src/components/RecentEvents.vue | 11 +-- apps/web/src/stores/users.ts | 32 +++------ apps/web/src/util/api.ts | 61 ----------------- packages/api-routes/index.ts | 1 + .../api/routes/bookcase/:username/options.ts | 2 +- .../api/routes/bookcase/:username/sort.ts | 2 +- .../coa/list/publications/:countrycode.ts | 2 +- .../routes/coa/stories/search/withIssues.ts | 2 +- .../contact-methods/:sellerId.ts | 2 +- .../routes/collection/subscriptions/:id.ts | 2 +- packages/api/routes/events.ts | 4 +- packages/types/Event.ts | 14 ++++ packages/types/events/Event.ts | 14 ---- 16 files changed, 119 insertions(+), 119 deletions(-) create mode 100644 apps/web/src/api.ts delete mode 100644 apps/web/src/util/api.ts create mode 100644 packages/types/Event.ts delete mode 100644 packages/types/events/Event.ts diff --git a/apps/web/index.ts b/apps/web/index.ts index a43e24f95..c1c18d283 100644 --- a/apps/web/index.ts +++ b/apps/web/index.ts @@ -1,11 +1,13 @@ +export { + createCachedCoaApi, + createCachedUserApi, + getCommonCacheOptions, +} from "./src/api"; import { bookcase } from "./src/stores/bookcase"; import { coa } from "./src/stores/coa"; -import * as apiUtil from "./src/util/api"; export const stores = { coa, bookcase, }; -export const util = apiUtil; - export { default as i18n } from "./src/i18n"; diff --git a/apps/web/src/App.vue b/apps/web/src/App.vue index e24c361c9..595717831 100644 --- a/apps/web/src/App.vue +++ b/apps/web/src/App.vue @@ -3,15 +3,22 @@ diff --git a/apps/web/src/api.ts b/apps/web/src/api.ts new file mode 100644 index 000000000..af17654b3 --- /dev/null +++ b/apps/web/src/api.ts @@ -0,0 +1,68 @@ +import axios from "axios"; +import { + CacheOptions, + CacheRequestConfig, + setupCache, +} from "axios-cache-interceptor"; +import dayjs from "dayjs"; + +import { addUrlParamsRequestInterceptor } from "~axios-helper"; + +const now = dayjs(); +const inAnHour = dayjs().add(1, "hour"); + +let coaCacheExpiration = dayjs(); +if (now.get("hour") >= 4) { + coaCacheExpiration = coaCacheExpiration.add(1, "day"); +} +coaCacheExpiration = coaCacheExpiration + .set("hour", 4) + .set("minute", 0) + .set("second", 0) + .set("millisecond", 0); + +export const getCommonCacheOptions = ( + storage: CacheOptions["storage"], +): CacheOptions => ({ + etag: false, + modifiedSince: false, + interpretHeader: false, + generateKey: (options: CacheRequestConfig) => + `${options.url}${ + options.params ? `?${new URLSearchParams(options.params).toString()}` : "" + }`, + storage: storage, +}); + +export const createCachedCoaApi = ( + storage: CacheOptions["storage"], + baseURL: string, +) => + addUrlParamsRequestInterceptor( + setupCache( + axios.create({ + baseURL, + }), + { + ...getCommonCacheOptions(storage), + methods: ["get", "post"], + ttl: coaCacheExpiration.diff(now), + }, + ), + ); + +export const createCachedUserApi = ( + storage: CacheOptions["storage"], + baseURL: string, +) => + addUrlParamsRequestInterceptor( + setupCache( + axios.create({ + baseURL, + }), + { + ...getCommonCacheOptions(storage), + ttl: inAnHour.diff(now), + }, + ), + ); diff --git a/apps/web/src/components/RecentEvents.vue b/apps/web/src/components/RecentEvents.vue index f57e77e7c..6afe876e7 100644 --- a/apps/web/src/components/RecentEvents.vue +++ b/apps/web/src/components/RecentEvents.vue @@ -42,8 +42,8 @@ const isCollectionUpdateEvent = (event: AbstractEvent) => const isEdgeCreationEvent = (event: AbstractEvent) => event.hasOwnProperty("edges"); -const fetchEventsAndAssociatedData = async (clearCacheEntry: boolean) => { - await users().fetchEvents(clearCacheEntry); +const fetchEventsAndAssociatedData = async () => { + await users().fetchEvents(); await coa().fetchPublicationNames([ ...events @@ -63,16 +63,11 @@ const fetchEventsAndAssociatedData = async (clearCacheEntry: boolean) => { await users().fetchStats( eventUserIds.filter((userId) => userId !== null) as number[], - clearCacheEntry, ); }; (async () => { - await fetchEventsAndAssociatedData(false); - setTimeout(async () => { - // await fetchEventsAndAssociatedData(true); - // hasFreshEvents = true; - }, 1000); + await fetchEventsAndAssociatedData(); isLoaded = true; })(); diff --git a/apps/web/src/stores/users.ts b/apps/web/src/stores/users.ts index ddba5fcd7..de890e774 100644 --- a/apps/web/src/stores/users.ts +++ b/apps/web/src/stores/users.ts @@ -1,8 +1,10 @@ import axios from "axios"; +import { buildWebStorage } from "axios-cache-interceptor"; import { defineStore } from "pinia"; -import { cachedUserApi as userApi } from "~/util/api"; +import { createCachedUserApi } from "~/api"; import { + GET__events, GET__global_stats__bookcase__contributors, GET__global_stats__user__$userIds, GET__global_stats__user__count, @@ -10,11 +12,11 @@ import { import { call } from "~axios-helper"; import { BookcaseContributor } from "~dm-types/BookcaseContributor"; import { AbstractEvent } from "~dm-types/events/AbstractEvent"; -import { BookstoreCommentEvent } from "~dm-types/events/BookstoreCommentEvent"; -import { CollectionSubscriptionAdditionEvent } from "~dm-types/events/CollectionSubscriptionAdditionEvent"; -import { CollectionUpdateEvent } from "~dm-types/events/CollectionUpdateEvent"; -import { EdgeCreationEvent } from "~dm-types/events/EdgeCreationEvent"; -import { SignupEvent } from "~dm-types/events/SignupEvent"; + +const cachedUserApi = createCachedUserApi( + buildWebStorage(sessionStorage), + import.meta.env.VITE_GATEWAY_URL, +); export const users = defineStore("users", () => { const count = ref(null as number | null), @@ -73,26 +75,12 @@ export const users = defineStore("users", () => { ).data; } }, - fetchEvents = async (clearCacheEntry = true) => { - const { data, cached } = await userApi.get( - "/events", - clearCacheEntry ? {} : { cache: false }, - ); - events.value = ( - data as ( - | BookstoreCommentEvent - | CollectionSubscriptionAdditionEvent - | CollectionUpdateEvent - | EdgeCreationEvent - | SignupEvent - )[] - ) + fetchEvents = async () => { + events.value = (await call(cachedUserApi, new GET__events())).data .sort(({ timestamp: timestamp1 }, { timestamp: timestamp2 }) => Math.sign(timestamp2 - timestamp1), ) .filter((_, index) => index < 50); - - return !cached; }; return { diff --git a/apps/web/src/util/api.ts b/apps/web/src/util/api.ts deleted file mode 100644 index c08d8aa7a..000000000 --- a/apps/web/src/util/api.ts +++ /dev/null @@ -1,61 +0,0 @@ -import axios from "axios"; -import { - buildWebStorage, - CacheRequestConfig, - setupCache, -} from "axios-cache-interceptor"; -import dayjs from "dayjs"; - -import { addUrlParamsRequestInterceptor } from "~axios-helper"; - -const customStorage = buildWebStorage(sessionStorage); - -const now = dayjs(); -const inAnHour = dayjs().add(1, "hour"); - -let coaCacheExpiration = dayjs(); -if (now.get("hour") >= 4) { - coaCacheExpiration = coaCacheExpiration.add(1, "day"); -} -coaCacheExpiration = coaCacheExpiration - .set("hour", 4) - .set("minute", 0) - .set("second", 0) - .set("millisecond", 0); - -const commonCacheOptions = { - etag: false, - modifiedSince: false, - interpretHeader: false, - generateKey: (options: CacheRequestConfig) => - `${options.url}${ - options.params ? `?${new URLSearchParams(options.params).toString()}` : "" - }`, - storage: customStorage, -}; - -const cachedUserApi = addUrlParamsRequestInterceptor( - setupCache( - axios.create({ - baseURL: import.meta.env.VITE_GATEWAY_URL, - }), - { - ...commonCacheOptions, - ttl: inAnHour.diff(now), - }, - ), -); - -const cachedCoaApi = addUrlParamsRequestInterceptor( - setupCache( - axios.create({ - baseURL: import.meta.env.VITE_GATEWAY_URL, - }), - { - ...commonCacheOptions, - ttl: coaCacheExpiration.diff(now), - }, - ), -); - -export { cachedCoaApi, cachedUserApi }; diff --git a/packages/api-routes/index.ts b/packages/api-routes/index.ts index 027f03cb3..1b0977609 100644 --- a/packages/api-routes/index.ts +++ b/packages/api-routes/index.ts @@ -9,6 +9,7 @@ import { CoverSearchResults } from "~dm-types/CoverSearchResults"; import { EdgeModel } from "~dm-types/EdgeModel"; import { EdgeWithModelId } from "~dm-types/EdgeWithModelId"; import { EditSubscription } from "~dm-types/EditSubscription"; +import { Event } from "~dm-types/Event"; import { ImageElement } from "~dm-types/ImageElement"; import { IssueCoverDetails } from "~dm-types/IssueCoverDetails"; import { } from "~dm-types/IssueSuggestionList"; diff --git a/packages/api/routes/bookcase/:username/options.ts b/packages/api/routes/bookcase/:username/options.ts index a371b17c8..239e77243 100644 --- a/packages/api/routes/bookcase/:username/options.ts +++ b/packages/api/routes/bookcase/:username/options.ts @@ -1,6 +1,6 @@ import { ExpressCall } from "~routes/_express-call"; -import { checkValidBookcaseUser } from "./index"; +import { checkValidBookcaseUser } from "."; export const get = async ( ...[req, res]: ExpressCall<{ diff --git a/packages/api/routes/bookcase/:username/sort.ts b/packages/api/routes/bookcase/:username/sort.ts index 97661f8be..fc125aeb2 100644 --- a/packages/api/routes/bookcase/:username/sort.ts +++ b/packages/api/routes/bookcase/:username/sort.ts @@ -1,7 +1,7 @@ import prismaDm from "~prisma-clients/extended/dm.extends"; import { ExpressCall } from "~routes/_express-call"; -import { checkValidBookcaseUser } from "./index"; +import { checkValidBookcaseUser } from "."; const getLastPublicationPosition = async (userId: number) => ( diff --git a/packages/api/routes/coa/list/publications/:countrycode.ts b/packages/api/routes/coa/list/publications/:countrycode.ts index 8b0a6bf95..310ac7b1f 100644 --- a/packages/api/routes/coa/list/publications/:countrycode.ts +++ b/packages/api/routes/coa/list/publications/:countrycode.ts @@ -3,7 +3,7 @@ import bodyParser from "body-parser"; import { PublicationTitles } from "~dm-types/PublicationTitles"; import { ExpressCall } from "~routes/_express-call"; -import { getPublicationTitles } from "./index"; +import { getPublicationTitles } from "."; const parseForm = bodyParser.json(); diff --git a/packages/api/routes/coa/stories/search/withIssues.ts b/packages/api/routes/coa/stories/search/withIssues.ts index aa2343418..fa165abaa 100644 --- a/packages/api/routes/coa/stories/search/withIssues.ts +++ b/packages/api/routes/coa/stories/search/withIssues.ts @@ -3,7 +3,7 @@ import bodyParser from "body-parser"; import { StorySearchResults } from "~dm-types/StorySearchResults"; import { ExpressCall } from "~routes/_express-call"; -import { getStoriesByKeywords } from "./index"; +import { getStoriesByKeywords } from "."; const parseForm = bodyParser.json(); diff --git a/packages/api/routes/collection/on-sale-by-others/contact-methods/:sellerId.ts b/packages/api/routes/collection/on-sale-by-others/contact-methods/:sellerId.ts index 5741647a6..f98dc0ddd 100644 --- a/packages/api/routes/collection/on-sale-by-others/contact-methods/:sellerId.ts +++ b/packages/api/routes/collection/on-sale-by-others/contact-methods/:sellerId.ts @@ -2,7 +2,7 @@ import { prismaDm } from "~/prisma"; import { userOptionType } from "~prisma-clients/client_dm"; import { ExpressCall } from "~routes/_express-call"; -import { getIssuesForSale } from "../index"; +import { getIssuesForSale } from ".."; export const get = async ( ...[req, res]: ExpressCall<{ diff --git a/packages/api/routes/collection/subscriptions/:id.ts b/packages/api/routes/collection/subscriptions/:id.ts index fdcfc483d..937dd2ecb 100644 --- a/packages/api/routes/collection/subscriptions/:id.ts +++ b/packages/api/routes/collection/subscriptions/:id.ts @@ -4,7 +4,7 @@ import { prismaDm } from "~/prisma"; import { EditSubscription } from "~dm-types/EditSubscription"; import { ExpressCall } from "~routes/_express-call"; -import { upsertSubscription } from "./index"; +import { upsertSubscription } from "."; const parseForm = bodyParser.json(); diff --git a/packages/api/routes/events.ts b/packages/api/routes/events.ts index a7c9b4cd6..52edfe3f1 100644 --- a/packages/api/routes/events.ts +++ b/packages/api/routes/events.ts @@ -1,6 +1,7 @@ import dayjs from "dayjs"; import { prismaDm } from "~/prisma"; +import { Event } from "~dm-types/Event"; import { AbstractEvent, AbstractEventRaw, @@ -18,7 +19,6 @@ import { EdgeCreationEvent, EdgeCreationEventRaw, } from "~dm-types/events/EdgeCreationEvent"; -import { Event } from "~dm-types/events/Event"; import { MedalEvent } from "~dm-types/events/MedalEvent"; import { SignupEvent } from "~dm-types/events/SignupEvent"; import { ExpressCall } from "~routes/_express-call"; @@ -54,7 +54,7 @@ const mapUsers = (event: AbstractEventRaw): T => event.users?.split(",")?.map((userId) => parseInt(userId)) || (event.userId && [event.userId]) || [], - } as T); + }) as T; const retrieveSignups = async (): Promise => ( diff --git a/packages/types/Event.ts b/packages/types/Event.ts new file mode 100644 index 000000000..6c822bded --- /dev/null +++ b/packages/types/Event.ts @@ -0,0 +1,14 @@ +import { BookstoreCommentEvent } from "./events/BookstoreCommentEvent"; +import { CollectionSubscriptionAdditionEvent } from "./events/CollectionSubscriptionAdditionEvent"; +import { CollectionUpdateEvent } from "./events/CollectionUpdateEvent"; +import { EdgeCreationEvent } from "./events/EdgeCreationEvent"; +import { MedalEvent } from "./events/MedalEvent"; +import { SignupEvent } from "./events/SignupEvent"; + +export type Event = + | SignupEvent + | CollectionUpdateEvent + | CollectionSubscriptionAdditionEvent + | BookstoreCommentEvent + | EdgeCreationEvent + | MedalEvent; diff --git a/packages/types/events/Event.ts b/packages/types/events/Event.ts deleted file mode 100644 index 1bba208f7..000000000 --- a/packages/types/events/Event.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BookstoreCommentEvent } from "./BookstoreCommentEvent"; -import { CollectionSubscriptionAdditionEvent } from "./CollectionSubscriptionAdditionEvent"; -import { CollectionUpdateEvent } from "./CollectionUpdateEvent"; -import { EdgeCreationEvent } from "./EdgeCreationEvent"; -import { MedalEvent } from "./MedalEvent"; -import { SignupEvent } from "./SignupEvent"; - -export type Event = - | SignupEvent - | CollectionUpdateEvent - | CollectionSubscriptionAdditionEvent - | BookstoreCommentEvent - | EdgeCreationEvent - | MedalEvent;