import type { FastifyInstance } from 'fastify'; import { get, query } from '../db/index.js'; import { cached } from '../cache.js'; // Item/recipe data only changes when game data is re-parsed (which calls // cacheClear()), so these can cache for a long time. const ITEM_TTL_MS = 60 * 60 * 1000; export async function itemRoutes(app: FastifyInstance): Promise { app.get('/api/items/search', async (req) => { const { q = '' } = req.query as { q?: string }; const queryStr = q.trim(); if (!queryStr) return { results: [] }; const results = await cached(`item:search:${queryStr.toLowerCase()}`, ITEM_TTL_MS, () => query( `SELECT unique_name, name_en, tier, enchantment, shop_category, shop_subcategory FROM items WHERE name_en ILIKE ? OR unique_name ILIKE ? ORDER BY tier, enchantment, unique_name LIMIT 50`, [`%${queryStr}%`, `%${queryStr}%`], ), ); return { results }; }); app.get('/api/items/:uniqueName', async (req, reply) => { const { uniqueName } = req.params as { uniqueName: string }; const data = await cached(`item:detail:${uniqueName}`, ITEM_TTL_MS, async () => { const item = await get('SELECT * FROM items WHERE unique_name = ?', [uniqueName]); if (!item) return null; const recipes = (await query('SELECT * FROM recipes WHERE output_item = ? ORDER BY source, variant', [uniqueName])) as any[]; for (const r of recipes) { r.inputs = await query( `SELECT ri.input_item, ri.count, ri.max_return_amount, i.name_en FROM recipe_inputs ri LEFT JOIN items i ON i.unique_name = ri.input_item WHERE ri.recipe_id = ?`, [r.id], ); } const usedIn = await query( `SELECT DISTINCT r.output_item, i.name_en FROM recipe_inputs ri JOIN recipes r ON r.id = ri.recipe_id LEFT JOIN items i ON i.unique_name = r.output_item WHERE ri.input_item = ? LIMIT 100`, [uniqueName], ); return { item, recipes, usedIn }; }); if (!data) return reply.code(404).send({ error: 'item not found' }); return data; }); }