diff options
-rw-r--r-- | src/hypixel.ts | 113 | ||||
-rw-r--r-- | src/index.ts | 2 | ||||
-rw-r--r-- | src/util.ts | 50 |
3 files changed, 73 insertions, 92 deletions
diff --git a/src/hypixel.ts b/src/hypixel.ts index 2e0c1bc..294432d 100644 --- a/src/hypixel.ts +++ b/src/hypixel.ts @@ -35,6 +35,7 @@ import { WithId } from 'mongodb' import { cleanEndedAuctions } from './cleaners/skyblock/endedAuctions.js' import { cleanAuctions } from './cleaners/skyblock/auctions.js' import { string } from 'prismarine-nbt' +import { withCache } from './util.js' export type Included = 'profiles' | 'player' | 'stats' | 'inventories' | undefined @@ -295,73 +296,31 @@ export async function fetchMemberProfilesUncached(playerUuid: string): Promise<C return profiles } -let isFetchingElection = false -let cachedElectionData: ElectionData | null = null -let nextElectionUpdate: Date = new Date(0) - export async function fetchElection(): Promise<ElectionData> { - if (cachedElectionData && nextElectionUpdate > new Date()) - return cachedElectionData - - // if it's currently fetching the election data and it doesn't have it, - // wait until we do have the election data - if (isFetchingElection && !cachedElectionData) { - await new Promise(resolve => { - const interval = setInterval(() => { - if (cachedElectionData) { - clearInterval(interval) - resolve(cachedElectionData) - } - }, 100) - }) - } - - isFetchingElection = true - const election: ElectionData = await sendCleanApiRequest( - 'resources/skyblock/election', - {} + return await withCache( + 'election', + (r) => new Date((r.lastUpdated + 60 * 60) * 1000), + async () => { + return await sendCleanApiRequest( + 'resources/skyblock/election', + {} + ) + } ) - isFetchingElection = false - - cachedElectionData = election - // updates every 10 minutes - nextElectionUpdate = new Date((election.lastUpdated + 10 * 60) * 1000) - return election } -let isFetchingItemList = false -let cachedItemListData: ItemListData | null = null -let nextItemListUpdate: Date = new Date(0) - export async function fetchItemList() { - if (cachedItemListData && nextItemListUpdate > new Date()) - return cachedItemListData - - // if it's currently fetching the election data and it doesn't have it, - // wait until we do have the election data - if (isFetchingItemList && !cachedItemListData) { - await new Promise(resolve => { - const interval = setInterval(() => { - if (cachedItemListData) { - clearInterval(interval) - resolve(cachedItemListData) - } - }, 100) - }) - } - - isFetchingItemList = true - const itemList: ItemListData = await sendCleanApiRequest( - 'resources/skyblock/items', - {} + return await withCache( + 'itemList', + (r) => new Date((r.lastUpdated + 60 * 60) * 1000), + async () => { + return await sendCleanApiRequest( + 'resources/skyblock/items', + {} + ) + } ) - isFetchingItemList = false - - cachedItemListData = itemList - // updates every 60 minutes - nextItemListUpdate = new Date((itemList.lastUpdated + 60 * 60) * 1000) - return itemList } export async function fetchAuctionUncached(uuid: string) { @@ -454,36 +413,12 @@ export async function periodicallyFetchRecentlyEndedAuctions() { } } -let isFetchingAuctionItemList = false -let cachedAuctionItemListData: Record<string, string> | null = null -let nextAuctionItemListUpdate: Date = new Date(0) - export async function fetchAuctionItems() { - if (cachedAuctionItemListData && nextAuctionItemListUpdate > new Date()) - return cachedAuctionItemListData - - // if it's currently fetching the election data and it doesn't have it, - // wait until we do have the election data - if (isFetchingAuctionItemList && !cachedAuctionItemListData) { - await new Promise(resolve => { - const interval = setInterval(() => { - if (cachedAuctionItemListData) { - clearInterval(interval) - resolve(cachedAuctionItemListData) - } - }, 100) - }) - } - - isFetchingAuctionItemList = true - const itemList = await fetchAuctionItemsUncached() - isFetchingAuctionItemList = false - if (!itemList) return undefined - - cachedAuctionItemListData = Object.fromEntries(itemList) - // updates every 60 minutes - nextAuctionItemListUpdate = new Date(Date.now() + 10 * 60 * 1000) - return cachedAuctionItemListData + return await withCache( + 'auctionItems', + 10 * 60 * 1000, + fetchAuctionItemsUncached + ) } async function fetchAuctionItemsUncached() { diff --git a/src/index.ts b/src/index.ts index f81190f..9de531f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -175,7 +175,6 @@ app.get('/auctionprices', async (req, res) => { }) try { res - .setHeader('Cache-Control', 'public, max-age=600') .json( itemIds ? await fetchItemsAuctions(itemIds) : await fetchPaginatedItemsAuctions(0, 100) ) @@ -188,7 +187,6 @@ app.get('/auctionprices', async (req, res) => { app.get('/auctionitems', async (req, res) => { try { res - .setHeader('Cache-Control', 'public, max-age=600') .json(await fetchAuctionItems()) } catch (err) { console.error(err) diff --git a/src/util.ts b/src/util.ts index 91f202d..9af95c7 100644 --- a/src/util.ts +++ b/src/util.ts @@ -103,4 +103,52 @@ export type RecursivePartial<T> = { T[P] extends (infer U)[] ? RecursivePartial<U>[] : T[P] extends object ? RecursivePartial<T[P]> : T[P] -}
\ No newline at end of file +} + +let caches: Map<string, { + data: any + isFetching: boolean + nextUpdate: Date +}> = new Map() + +export async function withCache<T>(key: string, ttl: number | ((arg: T) => Date), task: () => Promise<T>): Promise<T> { + if (caches.get(key)?.data && caches.get(key)!.nextUpdate > new Date()) + return caches.get(key)!.data + + // if it's currently fetching the election data and it doesn't have it, + // wait until we do have the election data + if (caches.get(key)?.isFetching && !caches.get(key)?.data) { + await new Promise(resolve => { + const interval = setInterval(() => { + if (caches.get(key)?.data) { + clearInterval(interval) + resolve(caches.get(key)!.data) + } + }, 100) + }) + } + + caches.set(key, { + ...(caches.get(key) ?? { data: undefined, nextUpdate: new Date(0) }), + isFetching: true, + }) + const data = await task() + caches.set(key, { + ...caches.get(key)!, + isFetching: false, + }) + if (!data) return undefined as any + + caches.set(key, { + ...caches.get(key)!, + data + }) + + const nextUpdate = typeof ttl === 'number' ? new Date(Date.now() + ttl) : ttl(data) + + caches.set(key, { + ...caches.get(key)!, + nextUpdate + }) + return data +} |