Frontend updates: caching basic data (json) from CDragon
Implement caching in the patch_detector, consume the cache from API routes in frontend
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
import { debounce, isEmpty } from '~/utils/helpers'
|
||||
|
||||
// Constants
|
||||
const CDRAGON_CHAMPIONS_URL =
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json'
|
||||
const CHAMPIONS_API_URL = '/api/champions'
|
||||
|
||||
// State
|
||||
@@ -11,7 +9,7 @@ const {
|
||||
data: championsData,
|
||||
pending: loadingChampions,
|
||||
error: championsError
|
||||
} = useFetch(CDRAGON_CHAMPIONS_URL, {
|
||||
} = useFetch('/api/cdragon/champion-summary', {
|
||||
key: 'champions-data',
|
||||
lazy: false,
|
||||
server: false // Disable server-side fetching to avoid hydration issues
|
||||
|
||||
@@ -13,16 +13,11 @@ const props = defineProps<{
|
||||
summonerSpells?: Array<{ id: number; count: number; pickrate: number }> // API data when available
|
||||
}>()
|
||||
|
||||
// Constants
|
||||
const ITEMS_API_URL = CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/items.json'
|
||||
const SUMMONER_SPELLS_URL =
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/summoner-spells.json'
|
||||
|
||||
// State
|
||||
const currentlySelectedBuild = ref(0)
|
||||
|
||||
// Fetch items
|
||||
const { data: items } = useFetch<Array<Item>>(ITEMS_API_URL, {
|
||||
// Fetch items from cached API
|
||||
const { data: items } = useFetch<Array<Item>>('/api/cdragon/items', {
|
||||
lazy: true,
|
||||
server: false
|
||||
})
|
||||
@@ -44,11 +39,14 @@ watch(
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// Fetch summoner spells
|
||||
const { data: summonerSpellsData } = useFetch<Array<SummonerSpell>>(SUMMONER_SPELLS_URL, {
|
||||
lazy: true,
|
||||
server: false
|
||||
})
|
||||
// Fetch summoner spells from cached API
|
||||
const { data: summonerSpellsData } = useFetch<Array<SummonerSpell>>(
|
||||
'/api/cdragon/summoner-spells',
|
||||
{
|
||||
lazy: true,
|
||||
server: false
|
||||
}
|
||||
)
|
||||
const summonerSpellMap = ref<Map<number, SummonerSpell>>(new Map())
|
||||
|
||||
watch(
|
||||
@@ -143,17 +141,13 @@ const primaryStyles: Ref<Array<PerkStyle>> = ref(Array(props.runes.length))
|
||||
const secondaryStyles: Ref<Array<PerkStyle>> = ref(Array(props.runes.length))
|
||||
const keystoneIds: Ref<Array<number>> = ref(Array(props.runes.length))
|
||||
|
||||
const { data: perks_data }: PerksResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perks.json'
|
||||
)
|
||||
const { data: perks_data }: PerksResponse = await useFetch('/api/cdragon/perks')
|
||||
const perks = reactive(new Map())
|
||||
for (const perk of perks_data.value) {
|
||||
perks.set(perk.id, perk)
|
||||
}
|
||||
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json'
|
||||
)
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch('/api/cdragon/perkstyles')
|
||||
|
||||
function refreshStylesKeystones() {
|
||||
for (const style of stylesData.value.styles) {
|
||||
|
||||
@@ -11,13 +11,10 @@ const emit = defineEmits<{
|
||||
refresh: []
|
||||
}>()
|
||||
|
||||
const { data: items } = useFetch<Array<{ id: number; iconPath: string }>>(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/items.json',
|
||||
{
|
||||
lazy: true, // Don't block rendering
|
||||
server: false // Client-side only
|
||||
}
|
||||
)
|
||||
const { data: items } = useFetch<Array<{ id: number; iconPath: string }>>('/api/cdragon/items', {
|
||||
lazy: true, // Don't block rendering
|
||||
server: false // Client-side only
|
||||
})
|
||||
|
||||
// Create item map reactively
|
||||
const itemMap = reactive(new Map<number, { id: number; iconPath: string }>())
|
||||
|
||||
@@ -7,15 +7,12 @@ const props = defineProps<{
|
||||
error?: boolean
|
||||
}>()
|
||||
|
||||
// Constants
|
||||
const ITEMS_API_URL = CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/items.json'
|
||||
|
||||
// State
|
||||
// State - use cached API endpoint instead of direct CDragon fetch
|
||||
const {
|
||||
data: items,
|
||||
pending: loadingItems,
|
||||
error: itemsError
|
||||
} = useFetch(ITEMS_API_URL, {
|
||||
} = useFetch('/api/cdragon/items', {
|
||||
lazy: true, // Don't block rendering
|
||||
server: false // Client-side only
|
||||
})
|
||||
|
||||
@@ -8,17 +8,13 @@ const props = defineProps<{
|
||||
const primaryStyle: Ref<PerkStyle> = ref({ id: 0, name: '', iconPath: '', slots: [] })
|
||||
const secondaryStyle: Ref<PerkStyle> = ref({ id: 0, name: '', iconPath: '', slots: [] })
|
||||
|
||||
const { data: perks_data }: PerksResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perks.json'
|
||||
)
|
||||
const { data: perks_data }: PerksResponse = await useFetch('/api/cdragon/perks')
|
||||
const perks = reactive(new Map())
|
||||
for (const perk of perks_data.value) {
|
||||
perks.set(perk.id, perk)
|
||||
}
|
||||
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json'
|
||||
)
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch('/api/cdragon/perkstyles')
|
||||
watch(
|
||||
() => props.primaryStyleId,
|
||||
async (_newP, _oldP) => {
|
||||
|
||||
@@ -14,17 +14,14 @@ const primaryStyles: Ref<Array<PerkStyle>> = ref(Array(props.runes.length))
|
||||
const secondaryStyles: Ref<Array<PerkStyle>> = ref(Array(props.runes.length))
|
||||
const keystoneIds: Ref<Array<number>> = ref(Array(props.runes.length))
|
||||
|
||||
const { data: perks_data }: PerksResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perks.json'
|
||||
)
|
||||
// Use cached API endpoints instead of direct CDragon fetch
|
||||
const { data: perks_data }: PerksResponse = await useFetch('/api/cdragon/perks')
|
||||
const perks = reactive(new Map())
|
||||
for (const perk of perks_data.value) {
|
||||
perks.set(perk.id, perk)
|
||||
}
|
||||
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json'
|
||||
)
|
||||
const { data: stylesData }: PerkStylesResponse = await useFetch('/api/cdragon/perkstyles')
|
||||
watch(
|
||||
() => props.runes,
|
||||
(_newRunes, _oldRunes) => {
|
||||
|
||||
@@ -4,9 +4,7 @@ import { LANE_IMAGES, lanePositionToIndex, POSITIONS_STR } from '~/utils/cdragon
|
||||
const route = useRoute()
|
||||
const lane = route.params.lane as string
|
||||
|
||||
const { data: championsData }: ChampionsResponse = await useFetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json'
|
||||
)
|
||||
const { data: championsData }: ChampionsResponse = await useFetch('/api/cdragon/champion-summary')
|
||||
|
||||
const { data: championsLanes }: { data: Ref<Array<ChampionData>> } =
|
||||
await useFetch('/api/champions')
|
||||
|
||||
14
frontend/server/api/cdragon/champion-summary.ts
Normal file
14
frontend/server/api/cdragon/champion-summary.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getChampionSummary } from '~/server/utils/cdragon-cache'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
const championSummary = await getChampionSummary()
|
||||
return championSummary
|
||||
} catch (error) {
|
||||
console.error('Error fetching champion summary:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch champion summary'
|
||||
})
|
||||
}
|
||||
})
|
||||
14
frontend/server/api/cdragon/items.ts
Normal file
14
frontend/server/api/cdragon/items.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getItems } from '~/server/utils/cdragon-cache'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
const items = await getItems()
|
||||
return items
|
||||
} catch (error) {
|
||||
console.error('Error fetching items:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch items'
|
||||
})
|
||||
}
|
||||
})
|
||||
14
frontend/server/api/cdragon/perks.ts
Normal file
14
frontend/server/api/cdragon/perks.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getPerks } from '~/server/utils/cdragon-cache'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
const perks = await getPerks()
|
||||
return perks
|
||||
} catch (error) {
|
||||
console.error('Error fetching perks:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch perks'
|
||||
})
|
||||
}
|
||||
})
|
||||
14
frontend/server/api/cdragon/perkstyles.ts
Normal file
14
frontend/server/api/cdragon/perkstyles.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getPerkStyles } from '~/server/utils/cdragon-cache'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
const perkStyles = await getPerkStyles()
|
||||
return perkStyles
|
||||
} catch (error) {
|
||||
console.error('Error fetching perk styles:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch perk styles'
|
||||
})
|
||||
}
|
||||
})
|
||||
14
frontend/server/api/cdragon/summoner-spells.ts
Normal file
14
frontend/server/api/cdragon/summoner-spells.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getSummonerSpells } from '~/server/utils/cdragon-cache'
|
||||
|
||||
export default defineEventHandler(async () => {
|
||||
try {
|
||||
const summonerSpells = await getSummonerSpells()
|
||||
return summonerSpells
|
||||
} catch (error) {
|
||||
console.error('Error fetching summoner spells:', error)
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to fetch summoner spells'
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,11 +1,7 @@
|
||||
import { CDRAGON_BASE } from '~/utils/cdragon'
|
||||
import { getChampionSummary } from '~/server/utils/cdragon-cache'
|
||||
|
||||
async function championRoutes() {
|
||||
const championsData: Array<Champion> = await (
|
||||
await fetch(
|
||||
CDRAGON_BASE + 'plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json'
|
||||
)
|
||||
).json()
|
||||
const championsData = await getChampionSummary()
|
||||
|
||||
const routes: Array<string> = []
|
||||
for (const champion of championsData) {
|
||||
|
||||
205
frontend/server/utils/cdragon-cache.ts
Normal file
205
frontend/server/utils/cdragon-cache.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user