pediatric-ai-scribe-v3/src/routes/adminMilestones.js
Daniel 9bfadd7344 Stop leaking e.message to clients across all routes
88 occurrences of res.status(500).json({ error: e.message }) (or
err.message) swept to generic 'Request failed'. Server-side
console.error / logger.error calls are untouched, so the full detail
still lands in logs and Grafana.

Covers: admin, adminConfig, adminMilestones, chartReview, documents,
encounters, hospitalCourse, hpi, learningAdmin, learningAI, learningHub,
logs, memories, milestones, oidc, refine, sessions, sickVisit, soap,
userPreferences, wellVisit.

Also extends .gitignore to exclude .env.backup-* files.
2026-04-14 03:04:24 +02:00

149 lines
4.3 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { adminMiddleware } = require('../middleware/auth');
const db = require('../db/database');
// Get all milestones (optionally filtered by age group)
router.get('/milestones', adminMiddleware, async (req, res) => {
try {
const { age_group } = req.query;
let query = 'SELECT * FROM developmental_milestones';
let params = [];
if (age_group) {
query += ' WHERE age_group = $1';
params.push(age_group);
}
query += ' ORDER BY age_group, domain, sort_order, id';
const result = await db.query(query, params);
res.json({ success: true, milestones: result.rows });
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
// Get unique age groups and domains
router.get('/milestones/meta', adminMiddleware, async (req, res) => {
try {
const ageGroups = await db.query(
'SELECT DISTINCT age_group FROM developmental_milestones ORDER BY age_group'
);
const domains = await db.query(
'SELECT DISTINCT domain FROM developmental_milestones ORDER BY domain'
);
res.json({
success: true,
age_groups: ageGroups.rows.map(r => r.age_group),
domains: domains.rows.map(r => r.domain)
});
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
// Create new milestone
router.post('/milestones', adminMiddleware, async (req, res) => {
try {
const { age_group, domain, milestone_text, sort_order } = req.body;
if (!age_group || !domain || !milestone_text) {
return res.status(400).json({ error: 'Missing required fields' });
}
const result = await db.query(
`INSERT INTO developmental_milestones (age_group, domain, milestone_text, sort_order)
VALUES ($1, $2, $3, $4)
RETURNING *`,
[age_group, domain, milestone_text, sort_order || 0]
);
res.json({ success: true, milestone: result.rows[0] });
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
// Update milestone
router.put('/milestones/:id', adminMiddleware, async (req, res) => {
try {
const { id } = req.params;
const { age_group, domain, milestone_text, sort_order } = req.body;
if (!age_group || !domain || !milestone_text) {
return res.status(400).json({ error: 'Missing required fields' });
}
const result = await db.query(
`UPDATE developmental_milestones
SET age_group = $1, domain = $2, milestone_text = $3, sort_order = $4, updated_at = NOW()
WHERE id = $5
RETURNING *`,
[age_group, domain, milestone_text, sort_order || 0, id]
);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Milestone not found' });
}
res.json({ success: true, milestone: result.rows[0] });
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
// Delete milestone
router.delete('/milestones/:id', adminMiddleware, async (req, res) => {
try {
const { id } = req.params;
const result = await db.query(
'DELETE FROM developmental_milestones WHERE id = $1 RETURNING *',
[id]
);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Milestone not found' });
}
res.json({ success: true, message: 'Milestone deleted' });
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
// Bulk import milestones (from static data)
router.post('/milestones/bulk-import', adminMiddleware, async (req, res) => {
try {
const { milestones, clearExisting } = req.body;
if (!Array.isArray(milestones)) {
return res.status(400).json({ error: 'Expected array of milestones' });
}
// Clear existing if requested
if (clearExisting) {
await db.query('DELETE FROM developmental_milestones');
}
let imported = 0;
for (const m of milestones) {
if (!m.age_group || !m.domain || !m.milestone_text) continue;
await db.query(
`INSERT INTO developmental_milestones (age_group, domain, milestone_text, sort_order)
VALUES ($1, $2, $3, $4)`,
[m.age_group, m.domain, m.milestone_text, m.sort_order || 0]
);
imported++;
}
res.json({ success: true, imported });
} catch (err) {
res.status(500).json({ error: 'Request failed' });
}
});
module.exports = router;