import { Router, Request, Response } from "express"; import { pool } from "../db"; import { rowToDto, bodyToColumns, RaceRow } from "../mappers/race"; const router = Router(); function dbError(res: Response) { res.status(503).json({ error: "database_unavailable" }); } /* ─── GET /races ──────────────────────────────────────────── */ router.get("/races", async (req: Request, res: Response) => { try { const { year, month } = req.query; const conditions: string[] = []; const params: unknown[] = []; let idx = 1; if (year) { conditions.push(`EXTRACT(YEAR FROM race_date) = $${idx++}`); params.push(Number(year)); } if (month) { conditions.push(`EXTRACT(MONTH FROM race_date) = $${idx++}`); params.push(Number(month)); } const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : ""; const sql = `SELECT * FROM races ${where} ORDER BY race_date ASC`; const { rows } = await pool.query(sql, params); res.json(rows.map(rowToDto)); } catch (err) { console.error("[GET /races]", err); dbError(res); } }); /* ─── GET /races/:id ──────────────────────────────────────── */ router.get("/races/:id", async (req: Request, res: Response) => { try { const { rows } = await pool.query( "SELECT * FROM races WHERE id = $1", [req.params.id], ); if (rows.length === 0) { res.status(404).json({ error: "not_found" }); return; } res.json(rowToDto(rows[0])); } catch (err) { console.error("[GET /races/:id]", err); dbError(res); } }); /* ─── POST /races ─────────────────────────────────────────── */ router.post("/races", async (req: Request, res: Response) => { const body = req.body; if (!body.id || !body.date || !body.title || body.distanceKm == null) { res.status(400).json({ error: "validation_error", details: ["Fields id, date, title, distanceKm are required"], }); return; } const { columns, values } = bodyToColumns(body); columns.unshift("id"); values.unshift(body.id); const placeholders = values.map((_, i) => `$${i + 1}`).join(", "); const sql = `INSERT INTO races (${columns.join(", ")}) VALUES (${placeholders}) RETURNING *`; try { const { rows } = await pool.query(sql, values); res.status(201).json(rowToDto(rows[0])); } catch (err: any) { if (err.code === "23505") { res.status(409).json({ error: "conflict", details: ["Race with this id already exists"] }); return; } console.error("[POST /races]", err); dbError(res); } }); /* ─── PATCH /races/:id ────────────────────────────────────── */ router.patch("/races/:id", async (req: Request, res: Response) => { const { columns, values } = bodyToColumns(req.body); if (columns.length === 0) { res.status(400).json({ error: "validation_error", details: ["No updatable fields provided"], }); return; } const sets = columns.map((col, i) => `${col} = $${i + 1}`); sets.push(`updated_at = NOW()`); values.push(req.params.id); const sql = `UPDATE races SET ${sets.join(", ")} WHERE id = $${values.length} RETURNING *`; try { const { rows } = await pool.query(sql, values); if (rows.length === 0) { res.status(404).json({ error: "not_found" }); return; } res.json(rowToDto(rows[0])); } catch (err) { console.error("[PATCH /races/:id]", err); dbError(res); } }); /* ─── DELETE /races/:id ───────────────────────────────────── */ router.delete("/races/:id", async (req: Request, res: Response) => { try { const { rowCount } = await pool.query( "DELETE FROM races WHERE id = $1", [req.params.id], ); if (rowCount === 0) { res.status(404).json({ error: "not_found" }); return; } res.status(204).end(); } catch (err) { console.error("[DELETE /races/:id]", err); dbError(res); } }); export default router;