// Task Team — Error Tracking — 2026-03-29 async function errorRoutes(app) { CREATE INDEX IF NOT EXISTS idx_errors_created ON error_logs(created_at DESC); `); // Log error from client app.post("/errors/report", async (req) => { const { message, stack, url, metadata } = req.body; await app.db.query( "INSERT INTO error_logs (message, stack, url, method, metadata) VALUES ($1,$2,$3,$4,$5)", [message || "Unknown error", stack || "", url || "", req.method, JSON.stringify(metadata || {})] ); return { status: "logged" }; }); // List recent errors (admin) app.get("/errors/recent", async (req) => { const { rows } = await app.db.query( "SELECT * FROM error_logs ORDER BY created_at DESC LIMIT 50" ); return { data: rows }; }); // Error stats app.get("/errors/stats", async (req) => { const { rows } = await app.db.query(` SELECT count(*) as total, count(*) FILTER (WHERE created_at > NOW() - INTERVAL '1 hour') as last_hour, count(*) FILTER (WHERE created_at > NOW() - INTERVAL '24 hours') as last_day FROM error_logs `); return { data: rows[0] }; }); // Global error handler for the API itself app.setErrorHandler(async (error, request, reply) => { app.log.error(error); try { await app.db.query( "INSERT INTO error_logs (level, message, stack, url, method) VALUES ($1,$2,$3,$4,$5)", [error.statusCode >= 500 ? "error" : "warn", error.message, error.stack || "", request.url, request.method] ); } catch (e) { // Silent catch - error logging should not break the response } reply.status(error.statusCode || 500).send({ error: error.message || "Internal Server Error", statusCode: error.statusCode || 500 }); }); } module.exports = errorRoutes;