albion/server/src/routes/items.ts
Заид Омар Медхат 337dac78ba
All checks were successful
Deploy / deploy (push) Successful in 3m58s
Client/server split, Postgres, auth, Windows agent, Docker deploy
- Extract world-parse + packet capture into @albion/shared (reused by
  server and a new standalone agent workspace).
- Migrate the data layer from SQLite/better-sqlite3 to shared Postgres
  (async pg with a ?->$n helper; economy engine preloads the item/recipe
  graph so its sync recursion still works).
- API-key auth (ALBION_API_KEY) on all /api routes except health; web UI
  sends it via a fetch wrapper + key-entry gate; server serves the built
  UI with a path-traversal-safe static handler (drops @fastify/static).
- Windows companion agent: pushes world map + captured market orders to
  /api/ingest/*; runs on the gaming PC where the game traffic lives.
- Docker + docker-compose (external proxy + backend networks for Nginx
  Proxy Manager and shared Postgres) and a Forgejo deploy workflow.
- Validated end-to-end against Postgres 16 and a full compose run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 04:10:08 +05:00

47 lines
1.7 KiB
TypeScript

import type { FastifyInstance } from 'fastify';
import { get, query } from '../db/index.js';
export async function itemRoutes(app: FastifyInstance): Promise<void> {
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 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 item = await get('SELECT * FROM items WHERE unique_name = ?', [uniqueName]);
if (!item) return reply.code(404).send({ error: 'item not found' });
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 };
});
}