import { Router, Request, Response } from 'express'; import { pool } from '../db'; import { asyncRoute } from '../asyncRoute'; import { PUBLIC_ITEM_SELECT } from '../itemSelect'; import { parseItemFilters, buildItemFilterSql, FilterError } from '../itemFilters'; const router = Router(); router.get('/', asyncRoute(async (req: Request, res: Response) => { let filters; try { filters = parseItemFilters(req.query as Record); } catch (err) { // A malformed filter is returned as an error rather than ignored, so a // broken link shows itself instead of quietly listing the whole catalogue. if (err instanceof FilterError) { return res.status(400).json({ error: err.message }); } throw err; } // 401 rather than an empty list: a signed-out visitor asking for "my // favorites" has no favorites to be empty of, and answering with [] would // render as "no items match these filters" — a plausible-looking lie. The // storefront prompts for sign-in instead of sending this, so reaching here // means a bookmarked link outlived its session. if (filters.favoritesOnly && !req.customerId) { return res.status(401).json({ error: 'sign in to filter by favorites' }); } const { clauses, params } = buildItemFilterSql(filters, 1, req.customerId ?? null); const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''; const { rows } = await pool.query(`${PUBLIC_ITEM_SELECT} ${where} ORDER BY i.created_at DESC`, params); res.json(rows); })); router.get('/:id', asyncRoute(async (req: Request, res: Response) => { const { rows } = await pool.query(`${PUBLIC_ITEM_SELECT} WHERE i.id = $1`, [req.params.id]); if (!rows.length) return res.status(404).json({ error: 'not found' }); res.json(rows[0]); })); export default router;