Implement caching in the patch_detector, consume the cache from API routes in frontend
206 lines
4.6 KiB
TypeScript
206 lines
4.6 KiB
TypeScript
import { readFile, existsSync } from 'fs'
|
|
import { join } from 'path'
|
|
import { promisify } from 'util'
|
|
|
|
const readFileAsync = promisify(readFile)
|
|
|
|
// CDragon base URL for fallback
|
|
const CDRAGON_BASE = 'https://raw.communitydragon.org/'
|
|
|
|
// Cache directory - can be configured via environment variable
|
|
// Default to dev/cdragon for development
|
|
const getCacheDir = () => {
|
|
if (process.env.CDRAGON_CACHE_DIR) {
|
|
return process.env.CDRAGON_CACHE_DIR
|
|
}
|
|
// Default to dev/cdragon relative to project root
|
|
return join(process.cwd(), '..', 'dev', 'data', 'cdragon')
|
|
}
|
|
|
|
/**
|
|
* Get the current patch from the patch.txt file or fallback to 'latest'
|
|
* Converts patch format for CDragon: "16.4.1" -> "16.4"
|
|
*/
|
|
async function getCurrentPatch(): Promise<string> {
|
|
const cacheDir = getCacheDir()
|
|
const patchFile = join(cacheDir, 'latest', 'patch.txt')
|
|
|
|
if (existsSync(patchFile)) {
|
|
const patch = await readFileAsync(patchFile, 'utf-8')
|
|
const trimmedPatch = patch.trim()
|
|
// Convert patch format for CDragon: "16.4.1" -> "16.4"
|
|
return trimmedPatch.split('.').slice(0, 2).join('.')
|
|
}
|
|
|
|
// Fallback to 'latest' if no patch file exists
|
|
return 'latest'
|
|
}
|
|
|
|
/**
|
|
* Cached CDragon data types
|
|
*/
|
|
interface CDragonCacheOptions {
|
|
patch?: string
|
|
}
|
|
|
|
/**
|
|
* Fetch data from local cache with CDragon fallback
|
|
*/
|
|
async function fetchFromCache<T>(
|
|
assetName: string,
|
|
cdragonPath: string,
|
|
options?: CDragonCacheOptions
|
|
): Promise<T> {
|
|
const cacheDir = getCacheDir()
|
|
const patch = options?.patch || (await getCurrentPatch())
|
|
const cachePath = join(cacheDir, patch, assetName)
|
|
|
|
// Try to read from cache first
|
|
if (existsSync(cachePath)) {
|
|
try {
|
|
const data = await readFileAsync(cachePath, 'utf-8')
|
|
return JSON.parse(data) as T
|
|
} catch (error) {
|
|
console.error(`Error reading cache file ${cachePath}:`, error)
|
|
}
|
|
}
|
|
|
|
// Fallback to CDragon
|
|
console.log(`Cache miss for ${assetName}, fetching from CDragon...`)
|
|
const url = `${CDRAGON_BASE}${patch}/${cdragonPath}`
|
|
const response = await fetch(url)
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch ${assetName} from CDragon: ${response.status}`)
|
|
}
|
|
|
|
return (await response.json()) as T
|
|
}
|
|
|
|
/**
|
|
* Get items data from cache
|
|
*/
|
|
async function getItems(patch?: string): Promise<CDragonItem[]> {
|
|
return fetchFromCache<CDragonItem[]>(
|
|
'items.json',
|
|
'plugins/rcp-be-lol-game-data/global/default/v1/items.json',
|
|
{ patch }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get perks (runes) data from cache
|
|
*/
|
|
async function getPerks(patch?: string): Promise<CDragonPerk[]> {
|
|
return fetchFromCache<CDragonPerk[]>(
|
|
'perks.json',
|
|
'plugins/rcp-be-lol-game-data/global/default/v1/perks.json',
|
|
{ patch }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get perk styles data from cache
|
|
*/
|
|
async function getPerkStyles(patch?: string): Promise<CDragonPerkStyles> {
|
|
return fetchFromCache<CDragonPerkStyles>(
|
|
'perkstyles.json',
|
|
'plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json',
|
|
{ patch }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get summoner spells data from cache
|
|
*/
|
|
async function getSummonerSpells(patch?: string): Promise<CDragonSummonerSpell[]> {
|
|
return fetchFromCache<CDragonSummonerSpell[]>(
|
|
'summoner-spells.json',
|
|
'plugins/rcp-be-lol-game-data/global/default/v1/summoner-spells.json',
|
|
{ patch }
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get champion summary data from cache
|
|
*/
|
|
async function getChampionSummary(patch?: string): Promise<CDragonChampionSummary[]> {
|
|
return fetchFromCache<CDragonChampionSummary[]>(
|
|
'champion-summary.json',
|
|
'plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json',
|
|
{ patch }
|
|
)
|
|
}
|
|
|
|
// Type definitions for CDragon data
|
|
interface CDragonItem {
|
|
id: number
|
|
name: string
|
|
iconPath: string
|
|
description?: string
|
|
plaintext?: string
|
|
into?: number[]
|
|
from?: number[]
|
|
gold?: {
|
|
base: number
|
|
total: number
|
|
sell: number
|
|
}
|
|
}
|
|
|
|
interface CDragonPerk {
|
|
id: number
|
|
name: string
|
|
iconPath: string
|
|
shortDesc?: string
|
|
longDesc?: string
|
|
}
|
|
|
|
interface CDragonPerkStyle {
|
|
id: number
|
|
name: string
|
|
iconPath: string
|
|
slots: Array<{
|
|
type: string
|
|
perks: number[]
|
|
}>
|
|
}
|
|
|
|
interface CDragonPerkStyles {
|
|
styles: CDragonPerkStyle[]
|
|
}
|
|
|
|
interface CDragonSummonerSpell {
|
|
id: number
|
|
name: string
|
|
iconPath: string
|
|
description?: string
|
|
}
|
|
|
|
interface CDragonChampionSummary {
|
|
id: number
|
|
name: string
|
|
alias: string
|
|
squarePortraitPath: string
|
|
roles?: string[]
|
|
}
|
|
|
|
export {
|
|
getItems,
|
|
getPerks,
|
|
getPerkStyles,
|
|
getSummonerSpells,
|
|
getChampionSummary,
|
|
getCurrentPatch,
|
|
CDRAGON_BASE
|
|
}
|
|
|
|
export type {
|
|
CDragonItem,
|
|
CDragonPerk,
|
|
CDragonPerkStyle,
|
|
CDragonPerkStyles,
|
|
CDragonSummonerSpell,
|
|
CDragonChampionSummary
|
|
}
|