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.
This commit is contained in:
Daniel 2026-04-14 03:04:24 +02:00
parent 263bd1a1c7
commit 54552df795
22 changed files with 96 additions and 94 deletions

2
.gitignore vendored
View file

@ -28,3 +28,5 @@ android/.idea/
*.keystore
*.jks
public/models/
.env.backup-*
*.env.backup*

View file

@ -23,7 +23,7 @@ router.get('/users', async function(req, res) {
[]
);
res.json({ success: true, users: users });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -45,7 +45,7 @@ router.get('/users/:id', async function(req, res) {
user.last_login = lastLogin ? lastLogin.timestamp : null;
res.json({ success: true, user: user });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -61,7 +61,7 @@ router.post('/users/:id/verify', async function(req, res) {
logger.audit(req.user.id, 'admin_verify_user', 'Verified user: ' + user.email, req, { category: 'admin' });
res.json({ success: true, message: user.email + ' verified' });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -84,7 +84,7 @@ router.post('/users/:id/disable', async function(req, res) {
logger.audit(req.user.id, 'admin_disable_user', 'Disabled user: ' + user.email, req, { category: 'admin' });
res.json({ success: true, message: user.email + ' disabled' });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -100,7 +100,7 @@ router.post('/users/:id/enable', async function(req, res) {
logger.audit(req.user.id, 'admin_enable_user', 'Enabled user: ' + user.email, req, { category: 'admin' });
res.json({ success: true, message: user.email + ' enabled' });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -128,7 +128,7 @@ router.post('/users/:id/role', async function(req, res) {
logger.audit(req.user.id, 'admin_change_role', 'Set ' + user.email + ' role to ' + role, req, { category: 'admin' });
res.json({ success: true, message: user.email + ' is now ' + role });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -151,7 +151,7 @@ router.delete('/users/:id', async function(req, res) {
logger.audit(req.user.id, 'admin_delete_user', 'Deleted user: ' + user.email + ' (' + user.name + ')', req, { category: 'admin' });
res.json({ success: true, message: user.email + ' deleted' });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -171,7 +171,7 @@ router.post('/users/:id/reset-password', async function(req, res) {
logger.audit(req.user.id, 'admin_reset_password', 'Reset password for: ' + user.email, req, { category: 'admin' });
res.json({ success: true, message: 'Password reset for ' + user.email });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -187,7 +187,7 @@ router.post('/settings/registration', async function(req, res) {
logger.audit(req.user.id, 'admin_toggle_registration', 'Registration ' + (enabled ? 'enabled' : 'disabled'), req, { category: 'admin' });
res.json({ success: true, registrationEnabled: enabled, message: 'Registration ' + (enabled ? 'enabled' : 'disabled') });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -211,7 +211,7 @@ router.get('/settings', async function(req, res) {
todayApiCalls: todayApiCount ? parseInt(todayApiCount.count) : 0
}
});
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -235,7 +235,7 @@ router.get('/logs/all', async function(req, res) {
var logs = await db.all(sql, params);
res.json({ success: true, logs: logs });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
module.exports = router;

View file

@ -23,7 +23,7 @@ router.get('/config/announcement', async function(req, res) {
text: text || '',
type: type || 'info'
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
router.use(adminMiddleware);
@ -45,7 +45,7 @@ router.get('/config', async function(req, res) {
}
});
res.json({ success: true, config: rows });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST send test email ───────────────────────────────────────────────────
@ -76,7 +76,7 @@ router.post('/config/test-email', async function(req, res) {
var ok = await sendEmail(to, '[TEST] ' + subject, html);
res.json({ success: ok, message: ok ? 'Test email sent to ' + to : 'SMTP not configured' });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET prompt list (for editor UI) ───────────────────────────────────────
@ -93,7 +93,7 @@ router.get('/config/prompts', async function(req, res) {
p.dbKey = dbKey;
});
res.json({ success: true, prompts: prompts });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST reset a prompt to hardcoded default ──────────────────────────────
@ -112,7 +112,7 @@ router.post('/config/prompts/:key/reset', async function(req, res) {
logger.audit(req.user.id, 'admin_config_reset', 'Reset prompt to default: ' + key, req, { category: 'admin' });
res.json({ success: true, value: PROMPTS[key] });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST reset all non-prompt settings to defaults ──────────────────────
@ -138,7 +138,7 @@ router.post('/config/reset-defaults', async function(req, res) {
}
logger.audit(req.user.id, 'admin_config_reset_all', 'Reset all settings to defaults', req, { category: 'admin' });
res.json({ success: true, message: 'Settings reset to defaults (SMTP and custom models preserved)' });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET SMTP status ──────────────────────────────────────────────────────
@ -158,7 +158,7 @@ router.get('/config/smtp/status', async function(req, res) {
from: from,
source: process.env.SMTP_HOST ? 'env' : (host ? 'database' : 'none')
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── PUT update SMTP settings ─────────────────────────────────────────────
@ -179,7 +179,7 @@ router.put('/config/smtp', async function(req, res) {
logger.audit(req.user.id, 'admin_smtp_update', 'Updated SMTP settings', req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── DELETE SMTP settings (clear DB override, fall back to env) ───────────
@ -191,7 +191,7 @@ router.delete('/config/smtp', async function(req, res) {
}
logger.audit(req.user.id, 'admin_smtp_clear', 'Cleared SMTP DB overrides', req, { category: 'admin' });
res.json({ success: true, message: 'SMTP DB settings cleared (env vars still apply if set)' });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -232,7 +232,7 @@ router.get('/config/models', async function(req, res) {
defaultModel: defaultModel,
litellmHint: activeProvider === 'litellm'
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── PUT toggle model enabled/disabled ────────────────────────────────────
@ -254,7 +254,7 @@ router.put('/config/models/toggle', async function(req, res) {
await db.setSetting('models.disabled', JSON.stringify(disabled));
logger.audit(req.user.id, 'admin_model_toggle', (enabled ? 'Enabled' : 'Disabled') + ' model: ' + modelId, req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── PUT set default model ─────────────────────────────────────────────────
@ -265,7 +265,7 @@ router.put('/config/models/default', async function(req, res) {
await db.setSetting('models.default', modelId.trim());
logger.audit(req.user.id, 'admin_model_default', 'Set default model: ' + modelId, req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST add custom model (manual entry) ─────────────────────────────────
@ -301,7 +301,7 @@ router.post('/config/models/custom', async function(req, res) {
await db.setSetting('models.custom', JSON.stringify(custom));
logger.audit(req.user.id, existing ? 'admin_model_update' : 'admin_model_add', (existing ? 'Updated' : 'Added') + ' custom model: ' + trimmedId, req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── DELETE custom model ───────────────────────────────────────────────────
@ -315,7 +315,7 @@ router.delete('/config/models/custom/:modelId(*)', async function(req, res) {
await db.setSetting('models.custom', JSON.stringify(custom));
logger.audit(req.user.id, 'admin_model_delete', 'Removed custom model: ' + modelId, req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST clear all custom/discovered models ───────────────────────────────
@ -326,7 +326,7 @@ router.post('/config/models/clear-all', async function(req, res) {
await db.setSetting('models.default', '');
logger.audit(req.user.id, 'admin_models_clear_all', 'Cleared all custom models and disabled list', req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET discover models from provider API ─────────────────────────────────
@ -341,7 +341,7 @@ router.get('/config/models/discover', async function(req, res) {
});
}
res.json({ success: true, models: discovered, count: discovered.length });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST add discovered model to custom list ──────────────────────────────
@ -366,7 +366,7 @@ router.post('/config/models/add-discovered', async function(req, res) {
await db.setSetting('models.custom', JSON.stringify(custom));
logger.audit(req.user.id, 'admin_model_discover_add', 'Added discovered model: ' + trimmedId, req, { category: 'admin' });
res.json({ success: true, id: trimmedId });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST test a model with a simple prompt ────────────────────────────────
@ -430,7 +430,7 @@ router.get('/config/tts', async function(req, res) {
elevenlabs: ['pNInz6obpgDQGcFmaJgB']
}
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET discover TTS voices from provider ────────────────────────────────
@ -513,7 +513,7 @@ router.get('/config/tts/discover', async function(req, res) {
});
}
res.json({ success: true, provider: provider, voices: discovered, count: discovered.length });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST test TTS — returns base64 audio ─────────────────────────────────
@ -614,7 +614,7 @@ router.get('/config/stt', async function(req, res) {
local: ['tiny', 'base', 'small', 'medium', 'large']
}
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET discover STT models from provider ────────────────────────────────
@ -695,7 +695,7 @@ router.get('/config/stt/discover', async function(req, res) {
});
}
res.json({ success: true, provider: provider, models: discovered, count: discovered.length });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST test STT — accepts base64 audio, returns transcription ───────────
@ -797,7 +797,7 @@ router.get('/config/embeddings', async function(req, res) {
{ id: 'text-embedding-ada-002', name: 'OpenAI text-embedding-ada-002', dims: 1536, tag: 'OPENAI' }
]
});
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── GET discover embedding models from provider ───────────────────────────
@ -872,7 +872,7 @@ router.get('/config/embeddings/discover', async function(req, res) {
});
}
res.json({ success: true, provider: provider, models: discovered, count: discovered.length });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── POST test embedding ───────────────────────────────────────────────────
@ -930,7 +930,7 @@ router.put('/config/:key(*)', async function(req, res) {
logger.audit(req.user.id, 'admin_config_update', 'Updated config: ' + key, req, { category: 'admin' });
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
module.exports = router;

View file

@ -20,7 +20,7 @@ router.get('/milestones', adminMiddleware, async (req, res) => {
const result = await db.query(query, params);
res.json({ success: true, milestones: result.rows });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -40,7 +40,7 @@ router.get('/milestones/meta', adminMiddleware, async (req, res) => {
domains: domains.rows.map(r => r.domain)
});
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -62,7 +62,7 @@ router.post('/milestones', adminMiddleware, async (req, res) => {
res.json({ success: true, milestone: result.rows[0] });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -90,7 +90,7 @@ router.put('/milestones/:id', adminMiddleware, async (req, res) => {
res.json({ success: true, milestone: result.rows[0] });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -110,7 +110,7 @@ router.delete('/milestones/:id', adminMiddleware, async (req, res) => {
res.json({ success: true, message: 'Milestone deleted' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -142,7 +142,7 @@ router.post('/milestones/bulk-import', adminMiddleware, async (req, res) => {
res.json({ success: true, imported });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -66,7 +66,7 @@ router.post('/generate-chart-review', authMiddleware, async (req, res) => {
res.json({ success: true, review: result.content, model: result.model });
logger.audit(req.user.id, 'generate_chart_review', 'Generated chart review', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -70,7 +70,7 @@ router.get('/documents', async function(req, res) {
[req.user.id]
);
res.json({ success: true, documents: rows, s3_configured: true });
} catch (e) { logger.error('GET /documents', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /documents', e.message); res.status(500).json({ error: 'Could not list documents' }); }
});
// ── POST upload document ───────────────────────────────────────────────
@ -107,7 +107,7 @@ router.post('/documents/upload', upload.single('file'), async function(req, res)
res.json({ success: true, id: result.lastInsertRowid, filename: req.file.originalname });
logger.audit(req.user.id, 'document_upload', 'Uploaded: ' + (req.file ? req.file.originalname : 'unknown'), req, { category: 'documents' });
} catch (e) { logger.error('POST /documents/upload', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('POST /documents/upload', e.message); res.status(500).json({ error: 'Upload failed' }); }
});
// ── GET download document (presigned URL) ──────────────────────────────
@ -132,7 +132,7 @@ router.get('/documents/:id/download', async function(req, res) {
res.json({ success: true, url: url });
logger.audit(req.user.id, 'document_download', 'Downloaded document ' + req.params.id, req, { category: 'documents' });
} catch (e) { logger.error('GET /documents/:id/download', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /documents/:id/download', e.message); res.status(500).json({ error: 'Download failed' }); }
});
// ── DELETE document ────────────────────────────────────────────────────
@ -158,7 +158,7 @@ router.delete('/documents/:id', async function(req, res) {
await db.run('DELETE FROM user_documents WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]);
res.json({ success: true });
logger.audit(req.user.id, 'document_delete', 'Deleted document ' + req.params.id, req, { category: 'documents' });
} catch (e) { logger.error('DELETE /documents/:id', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('DELETE /documents/:id', e.message); res.status(500).json({ error: 'Delete failed' }); }
});
module.exports = router;

View file

@ -18,7 +18,7 @@ router.get('/encounters/saved', async function(req, res) {
[req.user.id]
);
res.json({ success: true, encounters: rows });
} catch (e) { logger.error('GET /encounters/saved', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /encounters/saved', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── GET single saved encounter ───────────────────────────────────────────
@ -31,7 +31,7 @@ router.get('/encounters/saved/:id', async function(req, res) {
if (!row) return res.status(404).json({ error: 'Encounter not found or expired' });
logger.audit(req.user.id, 'encounter_load', 'Loaded encounter: ' + (row.label || 'unlabeled') + ' (id:' + req.params.id + ')', req, { category: 'clinical' });
res.json({ success: true, encounter: row });
} catch (e) { logger.error('GET /encounters/saved/:id', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /encounters/saved/:id', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── POST save/update encounter progress ─────────────────────────────────
@ -109,7 +109,7 @@ router.post('/encounters/saved', async function(req, res) {
res.json({ success: true, id: result.lastInsertRowid });
logger.audit(req.user.id, 'encounter_save', 'Saved encounter: ' + (label || 'unlabeled'), req, { category: 'clinical' });
}
} catch (e) { logger.error('POST /encounters/saved', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('POST /encounters/saved', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── DELETE saved encounter ───────────────────────────────────────────────
@ -122,7 +122,7 @@ router.delete('/encounters/saved/:id', async function(req, res) {
if (result.changes === 0) return res.status(404).json({ error: 'Not found' });
res.json({ success: true });
logger.audit(req.user.id, 'encounter_delete', 'Deleted encounter ' + req.params.id, req, { category: 'clinical' });
} catch (e) { logger.error('DELETE /encounters/saved/:id', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('DELETE /encounters/saved/:id', e.message); res.status(500).json({ error: 'Request failed' }); }
});
module.exports = router;

View file

@ -97,7 +97,7 @@ router.post('/generate-hospital-course', authMiddleware, async (req, res) => {
});
logger.audit(req.user.id, 'generate_hospital_course', 'Generated hospital course', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -113,7 +113,7 @@ router.post('/hospital-course-clarify', authMiddleware, async (req, res) => {
res.json({ success: true, questions: result.content, model: result.model });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -137,7 +137,7 @@ ${PROMPTS.refine.split('\n').slice(0, -1).join('\n')}` },
res.json({ success: true, hospitalCourse: result.content, model: result.model });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -24,7 +24,7 @@ router.post('/generate-hpi-encounter', authMiddleware, async (req, res) => {
res.json({ success: true, hpi: result.content, model: result.model });
logger.audit(req.user.id, 'generate_hpi_encounter', 'Generated HPI from encounter', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -47,7 +47,7 @@ router.post('/generate-hpi-dictation', authMiddleware, async (req, res) => {
res.json({ success: true, hpi: result.content, model: result.model });
logger.audit(req.user.id, 'generate_hpi_dictation', 'Generated HPI from dictation', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -325,7 +325,7 @@ router.post('/ai-generate', upload.array('files', 10), async function(req, res)
} catch (err) {
console.error('[LearningAI]', err.message);
res.status(500).json({ error: err.message || 'Generation failed' });
res.status(500).json({ error: 'Generation failed' });
}
});
@ -355,7 +355,7 @@ Return ONLY the refined HTML body (same structure, no JSON wrapper, no markdown
res.json({ success: true, refined, model: result.model });
} catch (err) {
console.error('[LearningAI]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -435,7 +435,7 @@ router.get('/webdav-browse', async function(req, res) {
res.json({ success: true, path: browsePath, parentPath, items });
} catch (err) {
console.error('[WebDAV]', err.message);
res.status(500).json({ error: 'WebDAV browse failed: ' + err.message });
res.status(500).json({ error: 'WebDAV browse failed' });
}
});
@ -448,7 +448,7 @@ router.post('/webdav-path', async function(req, res) {
await db.run('UPDATE users SET webdav_learning_path = ? WHERE id = ?', [wPath || null, req.user.id]);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -701,7 +701,7 @@ router.post('/generate-pptx', async function(req, res) {
} catch (err) {
console.error('[PPTX]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -728,7 +728,7 @@ router.post('/preview-slides', async function(req, res) {
res.json({ success: true, css: css, slides: slides });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -391,7 +391,7 @@ router.post('/embeddings/generate', async function(req, res) {
} catch (err) {
console.error('[LearningAdmin]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -412,7 +412,7 @@ router.get('/embeddings/status', async function(req, res) {
dimensions: parseInt(process.env.EMBEDDING_DIMENSIONS) || 768
});
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -139,7 +139,7 @@ router.get('/content/:slug/slides', async function(req, res) {
if (slides.length === 0) slides.push(html);
res.json({ success: true, css: css, slides: slides });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// ============================================================
@ -282,7 +282,7 @@ router.get('/search/semantic', async function(req, res) {
} catch (err) {
console.error('[LearningHub] Semantic search error:', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -362,7 +362,7 @@ router.get('/search/hybrid', async function(req, res) {
} catch (err) {
console.error('[LearningHub] Hybrid search error:', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -8,28 +8,28 @@ router.get('/logs/usage', authMiddleware, async function(req, res) {
var days = parseInt(req.query.days) || 30;
var summary = await logger.getUsageSummary(req.user.id, days);
res.json({ success: true, summary: summary, period: days + ' days' });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
router.get('/logs/audit', authMiddleware, async function(req, res) {
try {
var logs = await logger.getAuditLogs(req.user.id, parseInt(req.query.limit) || 50);
res.json({ success: true, logs: logs });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
router.get('/logs/api', authMiddleware, async function(req, res) {
try {
var logs = await logger.getApiLogs(req.user.id, parseInt(req.query.limit) || 50);
res.json({ success: true, logs: logs });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
router.get('/logs/access', authMiddleware, async function(req, res) {
try {
var logs = await logger.getAccessLogs(req.user.id, parseInt(req.query.limit) || 50);
res.json({ success: true, logs: logs });
} catch (err) { res.status(500).json({ error: err.message }); }
} catch (err) { res.status(500).json({ error: 'Request failed' }); }
});
// Client-side error receiver — no auth required (sendBeacon can't send auth headers)

View file

@ -24,7 +24,7 @@ router.get('/memories', async function(req, res) {
[req.user.id]
);
res.json({ success: true, memories: rows });
} catch (e) { logger.error('GET /memories', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /memories', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── POST create memory ───────────────────────────────────────────────────
@ -44,7 +44,7 @@ router.post('/memories', async function(req, res) {
[req.user.id, cat, name.trim().substring(0, 100), content.trim().substring(0, 5000)]
);
res.json({ success: true, id: result.lastInsertRowid });
} catch (e) { logger.error('POST /memories', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('POST /memories', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── PUT update memory ────────────────────────────────────────────────────
@ -63,7 +63,7 @@ router.put('/memories/:id', async function(req, res) {
]
);
res.json({ success: true });
} catch (e) { logger.error('PUT /memories/:id', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('PUT /memories/:id', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── DELETE memory ────────────────────────────────────────────────────────
@ -71,7 +71,7 @@ router.delete('/memories/:id', async function(req, res) {
try {
await db.run('DELETE FROM user_memories WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]);
res.json({ success: true });
} catch (e) { logger.error('DELETE /memories/:id', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('DELETE /memories/:id', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── GET memories as prompt context (for AI generation) ──────────────────
@ -119,7 +119,7 @@ router.get('/memories/context', async function(req, res) {
});
}
res.json({ success: true, context: context.trim() });
} catch (e) { logger.error('GET /memories/context', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('GET /memories/context', e.message); res.status(500).json({ error: 'Request failed' }); }
});
// ── POST auto-save correction (Dragon-like learning) ──────────────────
@ -156,7 +156,7 @@ router.post('/memories/correction', async function(req, res) {
[req.user.id, cat, name, content]
);
res.json({ success: true });
} catch (e) { logger.error('POST /memories/correction', e.message); res.status(500).json({ error: e.message }); }
} catch (e) { logger.error('POST /memories/correction', e.message); res.status(500).json({ error: 'Request failed' }); }
});
module.exports = router;

View file

@ -27,7 +27,7 @@ router.get('/milestones-data', authMiddleware, async (req, res) => {
res.json({ success: true, milestones: grouped });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -71,7 +71,7 @@ router.post('/generate-milestone-narrative', authMiddleware, async (req, res) =>
});
logger.audit(req.user.id, 'generate_milestone_narrative', 'Generated milestone narrative', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -89,7 +89,7 @@ router.post('/generate-milestone-summary', authMiddleware, async (req, res) => {
res.json({ success: true, summary: result.content, model: result.model });
logger.audit(req.user.id, 'generate_milestone_summary', 'Generated milestone summary', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -245,7 +245,7 @@ router.get('/oidc/config', authMiddleware, adminMiddleware, async function(req,
config['oidc.client_secret'] = '••••••••' + config['oidc.client_secret'].slice(-4);
}
res.json({ success: true, config: config });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
// ── Admin: PUT update OIDC config ───────────────────────────────────────
@ -264,7 +264,7 @@ router.put('/oidc/config', authMiddleware, adminMiddleware, async function(req,
}
res.json({ success: true });
} catch (e) { res.status(500).json({ error: e.message }); }
} catch (e) { res.status(500).json({ error: 'Request failed' }); }
});
module.exports = router;

View file

@ -34,7 +34,7 @@ router.post('/refine', authMiddleware, async function(req, res) {
res.json({ success: true, refined: result.content, model: result.model });
logger.audit(req.user.id, 'refine_document', 'Refined document', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -54,7 +54,7 @@ router.post('/shorten', authMiddleware, async function(req, res) {
res.json({ success: true, shortened: result.content, model: result.model });
logger.audit(req.user.id, 'shorten_document', 'Shortened document', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -75,7 +75,7 @@ router.post('/clarify', authMiddleware, async function(req, res) {
res.json({ success: true, questions: result.content, model: result.model });
logger.audit(req.user.id, 'clarify_document', 'Generated clarification questions', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -14,7 +14,7 @@ router.get('/', async function(req, res) {
);
res.json({ success: true, sessions: sessions, currentSessionId: req.sessionId || null });
} catch (e) {
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -31,7 +31,7 @@ router.delete('/:id', async function(req, res) {
if (result.changes === 0) return res.status(404).json({ error: 'Session not found' });
res.json({ success: true });
} catch (e) {
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -45,7 +45,7 @@ router.delete('/', async function(req, res) {
);
res.json({ success: true, revoked: result.changes });
} catch (e) {
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -67,7 +67,7 @@ router.post('/sick-visit/note', authMiddleware, async function(req, res) {
logger.audit(req.user.id, 'generate_sick_visit', 'Generated sick visit note', req, { category: 'clinical' });
} catch (e) {
logger.error('[SickVisit] Note generation failed', e.message);
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -32,7 +32,7 @@ router.post('/generate-soap', authMiddleware, async (req, res) => {
res.json({ success: true, soap: result.content, model: result.model });
logger.audit(req.user.id, 'generate_soap', 'Generated SOAP note', req, { category: 'clinical' });
} catch (err) {
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -20,7 +20,7 @@ router.get('/preferences', async function(req, res) {
});
} catch (err) {
console.error('[Preferences]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -37,7 +37,7 @@ router.post('/preferences', async function(req, res) {
res.json({ success: true });
} catch (err) {
console.error('[Preferences]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -150,7 +150,7 @@ router.get('/preferences/options', async function(req, res) {
} catch (err) {
console.error('[Preferences]', err.message);
res.status(500).json({ error: err.message });
res.status(500).json({ error: 'Request failed' });
}
});

View file

@ -126,7 +126,7 @@ router.post('/well-visit/shadess', authMiddleware, async function(req, res) {
logger.audit(req.user.id, 'generate_shadess', 'Generated SSHADESS assessment', req, { category: 'clinical' });
} catch (e) {
logger.error('[WellVisit] SHADESS generation failed', e.message);
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});
@ -228,7 +228,7 @@ router.post('/well-visit/note', authMiddleware, async function(req, res) {
logger.audit(req.user.id, 'generate_well_visit', 'Generated well visit note', req, { category: 'clinical' });
} catch (e) {
logger.error('[WellVisit] Note generation failed', e.message);
res.status(500).json({ error: e.message });
res.status(500).json({ error: 'Request failed' });
}
});