- Swagger UI at /docs (70+ routes documented) - Error tracking: error_logs table, /errors/report, /errors/recent, /errors/stats - Global error handler logs to DB - Email: nodemailer, /email/send, /email/reminder (SMTP placeholder) - Production keystore: RSA 2048, valid 2053, info.hasdo.taskteam Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
2.0 KiB
JavaScript
56 lines
2.0 KiB
JavaScript
// 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;
|